(本人本题完成于2016-7-19)
题目大意:给定一个字符串(长度为20*p,不超过200)和一个包含一些单词(个数为n,1≤n≤6)的词典,问如何将该字符串分成K(不超过40)份,使得每份中包含的单词个数之和最大,输出这个最大值。以一个位置为起始点只能统计一个单词。
做法:用a[i][j]表示区间(i,j)内所含的单词个数,可以用暴力计算出所有a[i][j]。再设f[i][j]为字符串前i个字符分割成j份的最优解,最终答案即是f[20*p][K]。状态转移方程为:f[i][j]=max(f[i][j],f[k][j-1]+a[k+1][i])。
以下是本人代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; long p,K,n,a[210][210]={0},f[210][210]={0}; //a,f的意义如上文所示 char w[10][210],s[210]; //w存储词典,s存储待处理的字符串 bool v[210]={0}; //v[i]表示当前以i为起始位置有没有统计过单词,值为1时代表已统计过 int main() { scanf("%ld %ld\n",&p,&K); for(int i=1;i<=p;i++) { for(int j=(i-1)*20+1;j<=i*20;j++) scanf("%c",&s[j]); scanf("\n"); } scanf("%ld\n",&n); for(int i=1;i<=n;i++) { scanf("%s\n",w[i]); for(int j=1;j<i;j++) if (!strcmp(w[i],w[j])) {i--;n--;break;} //比较,如果当前单词与已输入单词重复则舍去当前单词 } for(int i=1;i<=20*p;i++) for(int j=i;j<=20*p;j++) { memset(v,0,sizeof(v)); for(int k=1;k<=n;k++) { long len=strlen(w[k]); for(int ii=i;ii<=j-len+1;ii++) { if (v[ii]) continue; bool flag=1; for(int jj=0;jj<len;jj++) if (s[ii+jj]!=w[k][jj]) {flag=0;break;} if (flag) {a[i][j]++;v[ii]=1;} } } } //暴力求出所有a[i][j] for(int k=1;k<=K;k++) for(int i=1;i<=20*p;i++) for(int j=k-1;j<=i-1;j++) //注意从k-1开始,勿漏情况 f[i][k]=max(f[i][k],f[j][k-1]+a[j+1][i]); printf("%ld",f[20*p][K]); return 0; }