T1朴素的最長嚴格上升子序列
http://codevs.cn/problem/3955/
給一個數組a1, a2 ... an,找到最長的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk。
輸出長度即可。
第一行,一個整數N。
第二行 ,N個整數(N < = 1000000)
輸出K的極大值,即最長嚴格上升子序列的長度
5
9 3 6 2 7
3
n²做法:f[i]=j表示以第i個數結尾的最長上升子序列長度為j

#include<cstdio> #include<algorithm> using namespace std; int n,a[5001],f[5001],ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { int maxn=0; for(int j=1;j<i;j++) if(a[i]>a[j]&&maxn<f[j]) maxn=f[j]; f[i]=maxn+1; ans=max(f[i],ans); } printf("%d",ans); }
nlogn做法:f[i]=表示長度為i的最長上升子序列中,第i個數最小是j,二分查找

#include<cstdio> using namespace std; int n,f[5001],s,x; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); if(x>f[s]) { f[++s]=x; continue; } int l=0,r=s,k; while(l<=r) { int m=l+r>>1; if(x>f[m]) { l=m+1; k=m; } else r=m-1; } if(f[k+1]>x) f[k+1]=x; } printf("%d",s); }
T2 包含第k個數的最長上升子序列
http://codevs.cn/problem/2188/
LIS問題是最經典的動態規划基礎問題之一。如果要求一個滿足一定條件的最長上升子序列,你還能解決嗎?
給出一個長度為N整數序列,請求出它的包含第K個元素的最長上升子序列。
例如:對於長度為6的序列<2,7,3,4,8,5>,它的最長上升子序列為<2,3,4,5>,但如果限制一定要包含第2個元素,那么滿足此要求的最長上升子序列就只能是<2,7,8>了。
第一行為兩個整數N,K,如上所述。
接下來是N個整數,描述一個序列。
請輸出兩個整數,即包含第K個元素的最長上升子序列長度。
8 6
65 158 170 299 300 155 207 389
4
80%的數據,滿足0<n<=1000,0<k<=n
100%的數據,滿足0<n<=200000,0<k<=n
把k前面大於等於第k個數的都刪去,k后面小於等於第k個數的都刪去,然后套上面的方法

#include<cstdio> using namespace std; int n,a[200001],f[200001],s,k,x; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<k;i++) if(a[i]>=a[k]) a[i]=-1; for(int i=1;i<=n;i++) { if(a[i]==-1) continue; if(i>k&&a[i]<=a[k]) continue; if(a[i]>f[s]) { f[++s]=a[i]; continue; } int l=0,r=s,p=0; while(l<=r) { int m=l+r>>1; if(a[i]>f[m]) { l=m+1; p=m; } else r=m-1; } if(f[p+1]>a[i]) f[p+1]=a[i]; } printf("%d",s); }
兩個錯誤:
1、基本思路錯誤。錯誤代碼:

//f[i][j]只會在j時更新,而在j后面與j形成上升子序列的狀態不會使j更新。即f[i][j]只對j及其前面的答案正確。故此代碼錯誤
#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[1001],f[1001][1001],k,ans;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int maxn=0;
for(int j=1;j<i;j++)
if(a[i]>a[j])
{
maxn=max(maxn,f[j][0]);
f[i][j]=f[j][0]+1;
}
f[i][0]=f[i][i]=maxn+1;
}
for(int i=1;i<=n;i++)
ans=max(ans,f[i][k]);
printf("%d",ans);
}
2、二分查找中p沒有賦初值0
T3 最長上升子序列划分
http://codevs.cn/problem/4197/
給定一個長度為N(N為偶數)的序列,問能否將其划分為兩個長度為N/2的嚴格遞增子序列。
若干行,每行表示一組數據。對於每組數據,首先輸入一個整數N,表示序列的長度。之后N個整數表示這個序列。
同輸入行數。對於每組數據,如果存在一種划分,則輸出“Yes!”,否則輸出“No!“。
6 3 1 4 5 8 7
6 3 2 1 6 5 4
Yes!
No!
共兩組數據,每組數據行數<=50,0 <= 輸入的所有數 <= 10^9
第一組(30%):N <= 20
第二組(70%):N <= 2000
小提示:輸入部分可以用while(scanf(“%d”,&n)!=EOF)或者while not eof do read(n)
由觀察可以發現,若滿足此條件,則序列最長不上升子序列的長度<=2

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,a[2001],f[2001],p; int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); p=0; memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { int maxn=0; for(int j=1;j<i;j++) if(a[i]<=a[j]) maxn=max(maxn,f[j]); f[i]=maxn+1; } for(int i=1;i<=n;i++) p=max(p,f[i]); if(p>2) printf("No!\n"); else printf("Yes!\n"); } }
注:本解法有誤,反例已在評論中指出