作为NOIP中的知名的问题,最长不下降子序列可谓是灰常重要……
废话不多说,正题:
最长不下降有两种方法,然而都是DP……
1
裸DP,时间复杂度O(n^2):
定义:dp[ i ]=以a[ i ]为结尾的最长上升子序列的长度,so,dp[ i ]=max(1,dp[ j ]+1 | j < i且a[ j ] < a[ i ] );
代码:
1 int n; 2 int a[10000]; 3 int dp[10000]; 4 5 void solve(){ 6 int res=0; 7 for(int i=1;i<=n;i++){ 8 dp[i]=1; 9 for(int j=1;j<i;j++) if( a[ j ]<a[ i ] ){ 10 dp[i]=max(dp[i],dp[j]+1); 11 } 12 res=max(res,dp[i]); 13 } 14 cout<<res<<endl; 15 }
但…………此代码是我从书上抄的,因为…………我也不会…………我也不想学………………因为我会O(n log n)的……orz;
下面介绍O(n log n)的:
dp[1]=a[1];
从2————n for循环
我们定义dp[i]为长度为i的最长上升子序列的最后一个数的最小值,由此可推得dp是不下降的,每当我们发现了一个数比dp[len]大,就len++,dp[len]=这个数,否则,在dp中寻找一个dp[j-1]<这个数<dp[j] ,使dp[j]=min(dp[j],这个数),以此来维护正确性。又因dp是不下降的因此可以用二分求j。
代码:
1 int dp[1000] 2 void solve(){ 3 fill(dp+1,dp+n+1,INF); 4 for(int i=1;i<=n;i++){ 5 *lower_bound(dp+1,dp+1+n,a[i])=a[i]; 6 } 7 printf("%d",lower_bound(dp+1,dp+1+n,INF)-dp); 8 }
好吧,以上这个也是我抄的,但貌似不太靠谱,下一个是我的,反正比上面的靠谱点儿……
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 using namespace std; 8 int d[2000000],a[2000000],n,len,l,r,mid; 9 10 int main(){ 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 13 d[1]=a[1]; 14 len=1; 15 for (int i=2;i<=n;i++){ 16 if(a[i]>d[len]){ 17 len++; 18 d[len]=a[i]; 19 }else{ 20 l=1;r=len-1; 21 while(l<=r){ 22 mid=(l+r)/2; 23 if(d[mid]>=a[i]) r=mid-1; 24 else l=mid+1; 25 } 26 d[l]=min(a[i],d[l]); 27 } 28 } 29 printf("%d",len); 30 return 0; 31 }
上面的也不靠谱……
1 d[1]=a[1]; 2 int len=1; 3 for (int i=2;i<=n;i++){ 4 if (a[i]>d[len]) d[++len]=a[i]; 5 else{ 6 int j=upper_bound(d+1,d+len+1,a[i])-d; 7 d[j]=a[i]; 8 } 9 }