给定三个数字串A,B,C,请找到一个A,B的最长公共子序列,满足C是该子序列的子串。
标签:sample ++ break space etc cpp 位置 sam hint
找到的最长个公共子序列为(1,2,3,2)。
题解:我们先对于A和B的每个位置,处理出所有以i为结尾的,与C相同的子序列中,起始点最靠右的起始点位置pa[i]和pb[i]。然后处理出A和B的前缀最长公共子序列和后缀最长公共子序列f和g。用f[pa[i]-1][pb[j]-1]+g[i+1][j+1]更新答案即可。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int A,B,C,ans; int a[3010],b[3010],c[3010]; int f[3010][3010],g[3010][3010],pa[3010],pb[3010]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { int i,j,k; for(A=rd(),i=1;i<=A;i++) a[i]=rd(); for(B=rd(),i=1;i<=B;i++) b[i]=rd(); for(C=rd(),i=1;i<=C;i++) c[i]=rd(); for(i=1;i<=A;i++) for(j=1;j<=B;j++) { f[i][j]=max(f[i][j-1],f[i-1][j]); if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1); } if(!C) { printf("%d",f[A][B]); return 0; } for(i=A;i>=1;i--) for(j=B;j>=1;j--) { g[i][j]=max(g[i][j+1],g[i+1][j]); if(a[i]==b[j]) g[i][j]=max(g[i][j],g[i+1][j+1]+1); } for(i=1;i<=A;i++) { for(k=i,j=C;k>=1;k--) { if(a[k]==c[j]) j--; if(!j) break; } pa[i]=k-1; } for(i=1;i<=B;i++) { for(k=i,j=C;k>=1;k--) { if(b[k]==c[j]) j--; if(!j) break; } pb[i]=k-1; } for(i=1;i<=A;i++) for(j=1;j<=B;j++) if(pa[i]!=-1&&pb[j]!=-1) ans=max(ans,f[pa[i]][pb[j]]+g[i+1][j+1]+C); if(!ans) printf("-1"); else printf("%d",ans); return 0; }
【BZOJ4275】[ONTAK2015]Badania naukowe DP
标签:sample ++ break space etc cpp 位置 sam hint
原文地址:http://www.cnblogs.com/CQzhangyu/p/7749406.html