给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度。
如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5。
7 1 9 10 5 11 2 13 2 2 -1
5 1
这个题和前面做的那个单调递增子序列题目意思是一样的,但是这个题目数据量比较大,用前面的那种方法就会超时,前面的那一种方法dp[i]:用来储存以ai为末尾的最长递增子序列的长度;时间复杂度是o(n2),这个题目的数据范围到了100000,用这种方法就会超时;
所以我们采用另一种方法,用dp[i]:储存长度为i+1的递增序列中末尾元素的最小值;
建立一个一维数组dp[ ],用dp[i]保存长度为i的单调子序列的末尾元素的值,用top保存单调子序列的最大长度。
初始top=1,dp[1]=a1;
然后,我们从前向后遍历序列An(i : 2~n)。显然,我们会遇到两种情况:
1.a[i] > dp[top]。此时,我们把a[i]加入到长度为top的单调序列中,这时序列长度为top+1,top值也跟着更新,即dp[++top] = a[i];
2.a[i]<=dp[top]。此时,a[i]不能加入长度为top的单调序列,那它应该加入长度为多少的序列呢?
做法是,从后向前遍历dp[ ] (j: top-1~1),直到满足条件 a[i] > dp[j],此时,类似于步骤1,我们可以把a[i]加入到长度为j的单调序列中,这时此序列长度为j+1;(这段话转载于http://www.cnblogs.com/mycapple/archive/2012/08/22/2651453.html)
如果直接这样做的话,时间复杂度也会达到o(n2),但是这里还可以优化,我们在遍历更新的时候可以采用更高效率的方法;这里我们可以采用二分搜索,对前面的a[i]进行更新;这种方法的时间复杂度就是o(nlogn)
下面是代码:
#include <cstdio> #include <cstring> const int maxn=100001; int dp[maxn],a[maxn]; int Binary_search(int len,int k)//二分搜索 {//查找比第一个比dp[i]小或者是相等的位置 int start,end,mid; start=1; end=len; while(start<=end) { mid=(start+end)>>1; if(k==dp[mid]) return mid; if(k>dp[mid]) start=mid+1; else end=mid-1; } return start; } int main() { int n,i,t,len; while(scanf("%d",&n)!=EOF) { memset(dp,0,sizeof(dp)); for(i=0;i<n;i++) scanf("%d",&a[i]); len=1; dp[1]=a[0];//这里要从1开始 for(i=1;i<n;i++) { t=Binary_search(len,a[i]);//通过二分搜索查找a[i]要插入的位置 dp[t]=a[i];//更新dp的值 if(t>len)//如果插入的位置在最后,更新最大的长度 len=t; } printf("%d\n",len); } }
#include<cstdio> #include<algorithm> using namespace std; const int MAX=100100; int num[MAX],top=0; int main() { int n; while(~scanf("%d",&n)) { scanf("%d",&num[0]); top=1; for(int i=1;i!=n;i++) { scanf("%d",&num[i]); int * p=lower_bound(num,num+top,num[i]); if(p-num==top) ++top; *p=num[i]; } printf("%d\n",top); } }
nyist oj 214 单调递增子序列(二) (动态规划经典),布布扣,bubuko.com
nyist oj 214 单调递增子序列(二) (动态规划经典)
原文地址:http://blog.csdn.net/whjkm/article/details/38610747