最长上升子序列O(nlogn)算法详解
最长上升子序列
时间限制: 10 Sec 内存限制:128 MB
题目描述
给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。我们想知道此时最长上升子序列长度是多少?
输入
第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)
输出
1行,表示最长上升子序列的长度是多少。
样例输入
3
0 0 2
样例输出
2
提示
100%的数据 n<=100000
O(nlogn)算法代码:
1 #include <iostream> 2 using namespace std; 3 int i,j,n,s,t,a[100001]; 4 int main() 5 { 6 cin>>n; 7 a[0]=-1000000; 8 for(i=0;i<n;i++) 9 { 10 cin>>t;/* 比栈顶元素大数就入栈 */ 11 if(t>a[s]) a[++s]=t; 12 else 13 { 14 int l=1,h=s,m; 15 /* 二分检索栈中比t大的第一个数 */ 16 while(l<=h) 17 { 18 m=(l+h)/2; 19 if(t>a[m]) l=m+1; 20 else h=m-1; 21 }/* 用t替换 */ 22 a[l]=t; 23 } 24 }/* 最长序列数就是栈的大小 */ 25 cout<<s<<endl; 26 }
代码分析:
第一个念头就是用动态规划,很显然,这道题的转移方程非常非常简单,一目了然,先准备一个数组b
b[i]=1;
,从a[1]开始搜到i的最长上升子序列。
这句赋值语句固然很好理解,每一个元素,也可以视为一个符合题意的子序列。
b[2]呢?
如图,它显然比a[1]高,在执行如下语句时
for(j=1;j<i;j++) if(a[i]>a[j])
j小于i,也就是2,目前符合条件的只有a[1],a[1]又通过了判断语句,它确实小于a[i],执行下一条语句:
b[i]=max(b[i],b[j]+1);
很显然:b[2]显然原来是1,当它和b[1]+1比时,1当然比2小,所以,b[2]自然就是2了。
再来看看时间复杂度:
很明显,时间复杂度为O(n^2)。
那,这个方法够快吗?还可以,但仍然有些不尽人意。
代码如下O(n^2):
1 #include<iostream> 2 using namespace std; 3 int i,j,n,a[100],b[100],max; 4 int main() 5 { 6 cin>>n; 7 for(i=0;i<n;i++) cin>>a[i]; 8 b[0]=1; //初始化,以a[0]结尾的最长递增子序列长度为1 9 for(i=1;i<n;i++) 10 { 11 b[i]=1;//b[i]最小值为1 12 for(j=0;j<i;j++) 13 if(a[i]>a[j]) b[i]=max(b[i],b[j]+1); 14 } 15 for(max=i=0;i<n;i++) if(b[i]>max) max=b[i]; 16 cout<<max<<endl; 17 }