题目大意:给定n个单词,每个单词有一个分数,每次你可以打任意一个a~z的字符,每当纸带上出现一次某个单词,得分就加上这个分数,但是有时会打出不想打的字符,但这种情况最多发生k次,问最坏情况下他最多能得多少分
HINT:
原题面数据范围
测试点 1 – 2 满足,n = 1 或 k = 0;
测试点 1 – 6 满足,n ≤ 100,m ≤ 500,∑|Si| ≤ 500,A i ≤1000;
测试点 7 – 8 满足,k = 0,∑|Si| ≤ 200;
测试点 9 – 10 满足,∑|Si| ≤ 50,A i ≤1。
对于 100%的数据,n ≤ 100,m ≤ 10 9 ,∑|Si| ≤ 500,A i ≤1000,k ≤ 5。
首先我们捋一下题意:
最坏情况下,那一定是恰好发生k次打错字符的事情,而对于每个时刻的26种方案中,一定有一个最优情况,这个情况就是这个时刻想打出的字符,也就是说对于任何时刻,下一步的最优字符一定是已经确定的
所以我们可以先建立AC自动机,每个单词的终止节点拥有一个分数,然后考虑在上面做一些事情
对于1-6我们可以DP,设F[i][j][k]表示当前在i号节点,文章长度剩余j,最多还能犯k次错误的最大得分
那么怎么递推呢,假设他这次没犯错误,那么一定会选26种情况里获利最大的那个f[p][j-1][k] (p是最优情况)
但是他还有可能犯错,所以要和另外25种情况取个min f[p][j-1][k-1] (p不是最优情况)
这样我们可以得到60分
对于7-8,特殊点在于k=0,这样的话就不会有犯错的情况出现了想打什么就打什么,这样我们就可以用类似矩阵乘法快速幂的东西快速求解
对于9-10,有很多种做法,官方给的是利用ai<=1这点来找出最优比例环,然后一直在环上走这样的
然后xuruifan写了一个大概是大块矩阵乘法小块暴力DP的做法,也是能A的
只有我非常虎的写了个大块贪心小块DP,正确度无法保证(前面两个是有保证的),然后+1就过了
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 510
using namespace std;
char s[N];
int a[110];
int ch[N][27],nxt[N],la[N],w[N],cnt;
void addnew(int v)
{
int now=0;
int n=strlen(s),i,j,x;
for(i=0;i<n;i++)
{
x=s[i]-96;
if(!ch[now][x])
{
cnt++;
ch[now][x]=cnt;
}
now=ch[now][x];
}
w[now]+=v;
}
int q[N],h,t;
void getnxt()
{
h=1;t=0;
int i,j,x,y;
for(i=1;i<=26;i++)
if(ch[0][i]) t++,q[t]=ch[0][i];
while(h<=t)
{
x=q[h];h++;
for(i=1;i<=26;i++)
{
j=ch[x][i];
if(!j) {ch[x][i]=ch[nxt[x]][i];continue;}
y=nxt[x];
while(y&&!ch[y][i]) y=nxt[y];
nxt[j]=ch[y][i];
w[j]+=w[nxt[j]];
if(w[nxt[j]]) la[j]=nxt[j];
else la[j]=la[nxt[j]];
t++;q[t]=j;
}
}
}
int f[N][N][6];
bool used[N][N][6];
int K,m;
void dp(int n,int x,int k)
{
if(k>K||used[n][x][k]) return;
used[n][x][k]=true;
f[n][x][k]=w[x];
if(n==m) return;
int maxn=-707185547707185547LL,maxb=0;
int i,j;
for(i=1;i<=26;i++)
{
dp(n+1,ch[x][i],k);
if(k<K)
dp(n+1,ch[x][i],k+1);
if(f[n+1][ch[x][i]][k]>maxn)
{
maxn=f[n+1][ch[x][i]][k];
maxb=i;
}
}
maxn=707185547707185547LL;
for(i=1;i<=26;i++)
{
if(i==maxb) maxn=min(maxn,f[n+1][ch[x][i]][k]);
else if(k<K) maxn=min(maxn,f[n+1][ch[x][i]][k+1]);
}
f[n][x][k]+=maxn;
}
long long di[210][210],ret[210][210],tmp[210][210];
long long ans;
void do1(int M,bool chu)
{
ans=0;
int i,j,k;
if(chu)
{
memset(di,0xef,sizeof(di));
for(i=0;i<=cnt;i++)
for(j=1;j<=26;j++)
di[i][ch[i][j]]=w[ch[i][j]];
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
ret[i][j]=di[i][j];
M--;
while(M)
{
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
tmp[i][j]=-707185547707185547LL;
if(M&1)
{
for(i=0;i<=cnt;i++)
for(k=0;k<=cnt;k++)
for(j=0;j<=cnt;j++)
tmp[i][j]=max(ret[i][k]+di[k][j],tmp[i][j]);
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
ret[i][j]=tmp[i][j];
}
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
tmp[i][j]=-707185547707185547LL;
for(i=0;i<=cnt;i++)
for(k=0;k<=cnt;k++)
for(j=0;j<=cnt;j++)
tmp[i][j]=max(tmp[i][j],di[i][k]+di[k][j]);
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
di[i][j]=tmp[i][j];
M/=2;
}
memset(di,0xef,sizeof(di));
for(i=0;i<=cnt;i++)
for(j=1;j<=26;j++)
di[i][ch[i][j]]=w[ch[i][j]];
}
else
{
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
{
tmp[i][j]=-707185547707185547LL;
for(k=0;k<=cnt;k++)
tmp[i][j]=max(ret[i][k]+di[k][j],tmp[i][j]);
}
for(i=0;i<=cnt;i++)
for(j=0;j<=cnt;j++)
ret[i][j]=tmp[i][j];
}
for(i=0;i<=cnt;i++)
ans=max(ans,ret[0][i]);
}
void do2()
{
int M=m;
int P=M-300,i,j,k;
long long ret=0;
int st;
for(;P<=M-250;P++)
{
for(i=0;i<=300;i++)
for(j=0;j<=cnt;j++)
for(k=0;k<=K;k++)
used[i][j][k]=false;
m=M-P;
dp(0,0,0);
do1(P,(P==M-300));
ret=max(ret,f[0][0][0]+ans);
}
printf("%lld",ret+1);
}
int main()
{
int n;
scanf("%d%d%d",&n,&m,&K);
int i,j;
for(i=1;i<=n;i++)
{
scanf("%s%d",s,&a[i]);
addnew(a[i]);
}
getnxt();
if(m>500&&K==0&&cnt<=200) {do1(m,true);printf("%lld",ans);return 0;}
if(m>500&&K!=0&&cnt<=51) {do2();return 0;}
dp(0,0,0);
printf("%d",f[0][0][0]);
}