这道题DP或者记忆化搜索即可...
大多数的方法是设f[i][j][k][l]表示用了i张1,j张2,k张3,l张4的做法。
不过我就是丧心病狂地想搞成设f[i][j][k][l]表示第i格,用了j张2,k张3,l张4的做法。
大概要开到两千万的int数组,是不会MLE的。
只不过浪费了一堆空间(比如f[1][5][5][5]是不用到的)
而且这个程序很明显地会比其他的题解慢特别多...
#include <bits/stdc++.h>
using namespace std;
int f[351][41][41][41];
int v[351];
int maxa, maxb, maxc, maxd;
int dfs(int p, int b, int c, int d){
if(p<0 or b<0 or b>maxb or c<0 or c>maxc or d<0 or d>maxd or p-b*2-c*3-d*4-1<0 or p-b*2-c*3-d*4-1>maxa) //如果该状态不可能
return -1000000;
if(!(~f[p][b][c][d])) //即f[p][b][c][d]==-1
f[p][b][c][d]= max(dfs(p-1, b, c, d),
max(dfs(p-2, b-1, c, d),
max(dfs(p-3, b, c-1, d),
dfs(p-4, b, c, d-1))))+v[p];
return f[p][b][c][d];
}
int main(){
int i;
int n, m;
int tmp;
memset(f, 255, sizeof(f)); //初始化所有f为-1
scanf("%d %d", &n, &m);
for(i=1; i<=n; i++)
scanf("%d", &v[i]);
for(i=1; i<=m; i++){
scanf("%d", &tmp);
if(tmp==1) maxa++;
else if(tmp==2) maxb++;
else if(tmp==3) maxc++;
else maxd++;
}
f[1][0][0][0]=v[1];
printf("%d", dfs(n, maxb, maxc, maxd));
return 0;
}
坑点1:读入。这一题的读入很奇怪,是每行20个输入的,所以一定要记得判断换行。用快读的方式读入即可。
坑点2:题目理解。很多人可能都会理解成不能有重复字母的单词出现在同一段里,然而实际上说的是不能以同一个开头生成两个单词。(这就很简单了吧)
坑点3:DP边界。如楼下所述,f[i][j]表示前i个字符中分成j分最多有多少个单词。很多人习惯j从0开始循环。然而不分也至少有1份,所以不可能出现j==0的情况所以第三个数据点就炸了。然后,注意一下DP核心语句:
if(f[k][j-1])
f[i][j]=max(f[i][j],f[k][j-1]+ct(k+1,i));
若f[k][j-1]==0,则不能进入,因为等于0则不存在该状态,不应该进入计数;若计数,则ct就会得到错误的较大的答案,导致错误。这些细节还是得注意的。
#include<bits/stdc++.h>
using namespace std;
int len=0;
char zfc[300];
int p,k;
char wl;
void dr()//读入
{
while(p--)//p没有用了……
{
for(int i=0;i<20;i++)//每行20个
{
scanf(" %c",&wl);//注意有空格——" %c"
zfc[len++]=wl;//将新的字符加入到大字符串中
}
}
}
char dt[10][300]={0};//存每个可以使用的匹配单词,即字典
int gs;//字典单词个数
int kmp[10][300]={0};//kmp匹配函数
int ln[10];//存字典中每个单词的长度
bool visit[300];//防止重复统计,即题意中的“不能重复开头”
int xx[10];//每个单词目前匹配的位数,具体在KMP函数中
int sum;//统计一段单词中可以匹配的总个数
int ct(int zuo,int you)//KMP函数(匹配函数)
{
sum=0;
memset(visit,false,sizeof(visit));
memset(xx,0,sizeof(xx));
for(int i=zuo;i<=you;i++)
{
for(int j=0;j<gs;j++)
{
while(xx[j]&&zfc[i]!=dt[j][xx[j]])xx[j]=kmp[j][xx[j]];
if(zfc[i]==dt[j][xx[j]])xx[j]++;//KMP过程
if(xx[j]==ln[j]&&!visit[i-ln[j]+1])//已经匹配到全部,且开头未被统计过
{
visit[i-ln[j]+1]=true;
sum++;//计数
}
}
}
return sum;//返回计数
}
int main()
{
scanf("%d%d",&p,&k);
dr();
scanf("%d",&gs);
for(int i=0;i<gs;i++)scanf("%s",dt[i]);//读入
int x;
for(int i=0;i<gs;i++)//KMP匹配序列生成
{
kmp[i][0]=kmp[i][1]=0;
ln[i]=strlen(dt[i]);
x=0;
for(int j=1;j<ln[i];j++)
{
while(x&&dt[i][x]!=dt[i][j])x=kmp[i][x];
kmp[i][j+1]=dt[i][x]==dt[i][j]?++x:0;
}
}
int f[300][300]={0};
for(int i=0;i<len;i++)
{
for(int j=1;j<=k;j++)
{
if(j==1)
{
f[i][j]=ct(0,i);//一段直接匹配
}
else
{
for(int k=0;k<i;k++)
{
if(f[k][j-1])
f[i][j]=max(f[i][j],f[k][j-1]+ct(k+1,i));//取最大值
}
}
}
}
printf("%d",f[len-1][k]);//输出
return 0;
}