Codeforces 682D Alyona and Strings(DP)
题目大概说给两个字符串s和t,然后要求一个包含k个字符串的序列,而这个序列是两个字符串的公共子序列,问这个序列包含的字符串的总长最多是多少。
如果用DP解,考虑到问题的规模,自然这么表示状态:
- dp[i][j][k]表示s[0...i]与t[0...j]包含k个字符串的公共子序列的最大总长
想怎么转移时,我发现这样表示状态不好转移,还得加一维:
- dp[i][j][k][0]表示s[0...i]与t[0...j]包含k个字符串的公共子序列的最大总长,且s[i]和t[j]不属于序列第k个字符串
- dp[i][j][k][1]表示s[0...i]与t[0...j]包含k个字符串的公共子序列的最大总长,且s[i]和t[j]属于序列第k个字符串
转移的话:
- dp[i][j][k][1]能转移仅s[i]=t[j],可以通过在s[0...i-1]和t[0...j-1]的后面作为新加的序列字符串转移,即d[i-1][j-1][k-1]+1;也可以不作为新加的序列字符串,与前面的拼接转移,不过仅当s[i-1]=t[j-1],即dp[i-1][j-1][k]+1;
- dp[i][j][k][0]就是s[i]、t[j]忽略的情况,从dp[i-1][j][k]、dp[i][j-1][k]和dp[i][j][k]转移
转移感觉很麻烦,写着写着调着调着,终于过了样例,然后直接提交——居然就AC了?简直不敢相信。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int d[1111][1111][11][2]; 6 int main(){ 7 int n,m,K; 8 char s[1111]={0},t[1111]={0}; 9 scanf("%d%d%d",&n,&m,&K); 10 for(int i=0; i<n; ++i){ 11 scanf(" %c",&s[i]); 12 } 13 for(int i=0; i<m; ++i){ 14 scanf(" %c",&t[i]); 15 } 16 memset(d,0,sizeof(d)); 17 if(s[0]==t[0]) d[0][0][1][1]=1; 18 for(int i=0; i<n; ++i){ 19 for(int j=0; j<m; ++j){ 20 if(i==0 && j==0) continue; 21 for(int k=1; k<=K; ++k){ 22 if(i==0){ 23 if(s[i]==t[j]){ 24 d[i][j][1][1]=1; 25 } 26 d[i][j][1][0]=max(d[i][j-1][1][0],d[i][j-1][1][1]); 27 }else if(j==0){ 28 if(s[i]==t[j]){ 29 d[i][j][1][1]=1; 30 } 31 d[i][j][1][0]=max(d[i-1][j][1][0],d[i-1][j][1][1]); 32 }else{ 33 if(s[i]==t[j]){ 34 d[i][j][k][1]=max(d[i-1][j-1][k-1][0],d[i-1][j-1][k-1][1])+1; 35 if(s[i-1]==t[j-1]) d[i][j][k][1]=max(d[i][j][k][1],d[i-1][j-1][k][1]+1); 36 } 37 d[i][j][k][0]=max(d[i-1][j][k][0],d[i][j-1][k][0]); 38 d[i][j][k][0]=max(d[i][j][k][0],d[i][j-1][k][1]); 39 d[i][j][k][0]=max(d[i][j][k][0],d[i-1][j][k][1]); 40 d[i][j][k][0]=max(d[i][j][k][0],d[i-1][j-1][k][0]); 41 if(s[i-1]==t[j-1]) d[i][j][k][0]=max(d[i][j][k][0],d[i-1][j-1][k][1]); 42 } 43 } 44 } 45 } 46 printf("%d",max(d[n-1][m-1][K][0],d[n-1][m-1][K][1])); 47 return 0; 48 }