【BZOJ4275】[ONTAK2015]Badania naukowe DP
【BZOJ4275】[ONTAK2015]Badania naukowe
Description
给定三个数字串A,B,C,请找到一个A,B的最长公共子序列,满足C是该子序列的子串。
Input
第一行包含一个正整数n(1<=n<=3000),表示A串的长度。
第二行包含n个正整数,其中第i个数表示A[i](1<=A[i]<=1000)。
第三行包含一个正整数m(1<=m<=3000),表示B串的长度。
第四行包含m个正整数,其中第i个数表示B[i](1<=B[i]<=1000)。
第五行包含一个整数k(0<=k<=3000),表示C串的长度。
第六行包含k个正整数,其中第i个数表示C[i](1<=C[i]<=1000)。
Output
输出一个整数,即满足条件的最长公共子序列的长度,如果无解输出-1。特别的,如果k为0且无解,请输出0。
Sample Input
7
1 2 2 3 1 1 2
6
1 2 1 3 1 2
2
3 2
1 2 2 3 1 1 2
6
1 2 1 3 1 2
2
3 2
Sample Output
4
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; }
| 欢迎来原网站坐坐! >原文链接<