我out了,想不到最长上升子序列还有nlogn的算法,看来以前学得不够仔细,于是把代码贴出来学习一下。
参考:http://blog.youkuaiyun.com/Hashmat/article/details/5883605
/**
设A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] =
0(t = 1, 2, ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A
[j] < A[t])。
根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满
足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
注意到D[]的两个特点:
(1) D[k]的值是在整个计算过程中是单调不上升的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。
需要证明一下
假设d[2] >= d[3]:
a. d[2] 在 d[3] 前面,发现d[3]前的一个元素可以代替d[2]
b. d[2] 在 d[3] 后面,发现d[2]不成立.
于是,主要的工作就是维护这个单调的队列。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define P 40010
#define hpmax(a,b) ((a)>(b)?(a):(b))
int D[P], map[P];
int p;
int find( int len, int value ) //寻找第一个大于或等于value的下标
{
int i, j, mid;
i = 1;
j = len;
while ( i <= j ) {
mid = (i+j)>>1;
if ( D[mid] > value )
j = mid-1;
else if ( D[mid] < value )
i = mid+1;
else
return mid;
}
return i;
}
int main()
{
int t, i, j, k;
scanf("%d", &t );
while ( t -- ) {
scanf("%d", &p);
for ( i = 1; i <= p; ++ i ) {
scanf("%d", &map[i] );
}
D[1] = map[1];
j = 1;
for ( i = 2; i <= p; ++ i ) {
if ( map[i] > D[j] ) {
D[++j] = map[i];
}
else {
k = find( j, map[i] );
D[k] = map[i];
}
}
printf("%d\n", j );
}
return 0;
}