BZOJ4629: [BeiJing2016]打字机

26 篇文章 0 订阅
9 篇文章 0 订阅

题目大意:给定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]);
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值