序列型动规模板
最长上升子序列(lis)
1.O(n^2)
1 #include<cstdio> 2 #include<algorithm> 3 using std :: max; 4 5 const int maxn=100005; 6 int a[maxn],dp[maxn],pre[maxn]; 7 int n; 8 9 void outit(int now)//输出lis 10 { 11 if(pre[now]) outit(pre[now]); 12 printf("%d ",a[now]); 13 } 14 15 void solve() 16 { 17 for(int i=1;i<=n;i++) 18 { 19 dp[i]=1;pre[i]=0; 20 for(int j=1;j<i;j++) 21 { 22 if(a[j]<a[i]&&dp[j]+1>dp[i]) 23 { 24 dp[i]=dp[j]+1; 25 pre[i]=j; 26 } 27 } 28 } 29 int ans=0, last; 30 for(int i=1;i<=n;i++) 31 { 32 if(dp[i]>ans) 33 { 34 ans=dp[i]; 35 last=i; 36 } 37 } 38 printf("%d\n",ans); 39 outit(last); 40 } 41 42 void init() 43 { 44 scanf("%d",&n); 45 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 46 } 47 48 int main() 49 { 50 freopen("lis.in","r",stdin); 51 freopen("lis.out","w",stdout); 52 init(); 53 solve(); 54 fclose(stdin); 55 fclose(stdout); 56 return 0; 57 }
2.O(nlogn)
1 #include<cstdio> 2 #include<algorithm> 3 4 const int maxn=10005,INF=0x7fffffff; 5 struct node 6 { 7 int x,num; 8 node(){} 9 node(int a,int b) : x(a), num(b) {} 10 bool operator < (const node &a) const { return x>a.x; } 11 }dp[maxn]; 12 int a[maxn],pre[maxn]; 13 int n; 14 15 int bsearch(int l,int r,int v) 16 { 17 while(l<r) 18 { 19 int m=l+(r-l)/2; 20 if(dp[m].x>=v) r=m; 21 else l=m+1; 22 } 23 return l; 24 } 25 26 void outit(int now)//输出lis 27 { 28 if(pre[now]) outit(pre[now]); 29 printf("%d ",a[now]); 30 } 31 32 void solve() 33 { 34 for(int i=1;i<=n+1;i++) dp[i].x=INF; 35 for(int i=1;i<=n;i++) 36 { 37 int idx=bsearch(1,n,a[i]); 38 dp[idx].x=a[i]; 39 dp[idx].num=i; 40 pre[i]=dp[idx-1].num; 41 } 42 int ans=bsearch(1,n+1,INF)-1; 43 printf("%d\n",ans); 44 outit(dp[ans].num); 45 } 46 47 void init() 48 { 49 scanf("%d",&n); 50 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 51 } 52 53 int main() 54 { 55 freopen("lis.in","r",stdin); 56 freopen("lis.out","w",stdout); 57 init(); 58 solve(); 59 fclose(stdin); 60 fclose(stdout); 61 return 0; 62 }
最长公共子序列(lcs)
O(n^2).暂时没有输出lcs的部分.
1 #include<cstdio> 2 #include<algorithm> 3 using std :: max; 4 5 const int maxn=10005; 6 int n,m; 7 int a[maxn],b[maxn]; 8 int dp[maxn][maxn]; 9 10 void solve() 11 { 12 for(int i=1;i<=n;i++) 13 { 14 for(int j=1;j<=m;j++) 15 { 16 if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1; 17 else dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 18 } 19 } 20 printf("%d\n",dp[n][m]); 21 } 22 23 void init() 24 { 25 scanf("%d%d",&n,&m); 26 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 27 for(int i=1;i<=m;i++) scanf("%d",&b[i]); 28 } 29 30 int main() 31 { 32 freopen("lcs.in","r",stdin); 33 freopen("lcs.out","w",stdout); 34 init(); 35 solve(); 36 fclose(stdin); 37 fclose(stdout); 38 return 0; 39 }
最长公共上升子序列(lcis)
状态转移方程:
a[i]==b[j]时:dp[j]=max(dp[k]+1) (1<=k<j&&b[k]<b[j])
dp[j]表示以b[j]结尾的lcis,a串最为外层循环,当a串到a[i]位置,b串到b[j]位置时,dp[j]存储的是a串在a[i-1]位置,b串在b[j]位置时的最优值.此时判断a[i]是否等于b[j]:
1.a[i]!=b[j],现在的最优值等于a在a[i-1]位置时的最优值,即dp[j]不变.
2.a[i]==b[j],dp[j]=max(dp[k]+1) (1<=k<j&&b[k]<b[j]).
对于这个k,如果到了a[i]==b[j]时再返回从b[1]~b[j-1]找就O(n^3)了,所以在到达b[j]之前就记录最优的dp[k]与k.a[i]==b[j]时,b[k]<b[j]=a[i],
所以在a[i]的循环中,对于b串当前的b[j]位置,如果b[j]<a[i]&&maxk(最优dp[k])<dp[j],那么maxk=dp[j],k=j.
ps.因为要输出lcis,所以ans不记录最优dp[j],而是最优dp[j]对应的j.
1 #include<cstdio> 2 3 const int maxn=10005; 4 int n,m; 5 int a[maxn],b[maxn],dp[maxn],pre[maxn]; 6 7 void print_lcis(int now)//输出lcis 8 { 9 if(pre[now]) print_lcis(pre[now]); 10 printf("%d ",b[now]); 11 } 12 13 void solve() 14 { 15 int ans;//lcis在b中的末位置 16 for(int i=1;i<=n;i++) 17 { 18 int maxk=0,k=0;//到当前j为止满足1<=k<j且b[k]<b[j]的dp[k]的值以及k 19 for(int j=1;j<=m;j++) 20 { 21 if(b[j]<a[i]&&dp[j]>maxk) { maxk=dp[j]; k=j; } 22 if(b[j]==a[i]) 23 { 24 dp[j]=maxk+1; 25 pre[j]=k; 26 if(dp[j]>dp[ans]) ans=j; 27 } 28 } 29 } 30 printf("%d\n",dp[ans]); 31 print_lcis(ans); 32 } 33 34 void init() 35 { 36 scanf("%d%d",&n,&m); 37 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 38 for(int i=1;i<=m;i++) scanf("%d",&b[i]); 39 } 40 41 int main() 42 { 43 freopen("lcis.in","r",stdin); 44 freopen("lcis.out","w",stdout); 45 init(); 46 solve(); 47 fclose(stdin); 48 fclose(stdout); 49 return 0; 50 }