Lazy Narek CodeForces - 2005C分析与解答

简化价值计算:对某个字符串,先扫描一遍,出现narek中的字符则该字符串花销cost[i]增加1

当成功连成“narek”时,将价值增加10

由于关心分别以n a r e k结尾的字符串的价值可以用来转移,故用dp[i][j]表示第1到 i 个字符串拼成的结尾是 j 的"nareknarek..."串的价值

dp数组计算方式:对一个字符串 i,如果不选,则dp[i][j]=dp[i-1][j],j依次是n a r e k

如果选了,则枚举选了这个字符串后,开头的字符是什么(n a r e k依次枚举),从这个字符串第一个出现选定的开头字符的位置开始,往后查看"nareknareknarek...."序列,如果找到了k将价值加10(每个字符串价值初始为0),最后停在字母 j 上,则dp[i][j]可以被:dp[i-1][ j在narek中的上一个字母 ]+当前字符串的价值+cost[i]更新


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

#define ll long long

const ll maxn=1e3+5,inf=1e9;

ll n,m;
ll cost[maxn],dp[maxn][10];
char word[10]={'@','n','a','r','e','k'};

int main()
{
    freopen("D:\\in.txt","r",stdin);
    //ios::sync_with_stdio(0);cin.tie(0);

    ll T;cin>>T;
    while(T--){
        cin>>n>>m;

        for(ll j=1;j<=5;j++){
            dp[0][j]=-inf;
        }
        dp[0][5]=0;
        for(ll i=1;i<=n;i++){
            string s;cin>>s;
            cost[i]=0;
            //计算cost
            for(ll j=0;j<m;j++){
                for(ll k=1;k<=5;k++) {
                    if(s[j]==word[k]) cost[i]++;
                }
            }
            for(ll j=1;j<=5;j++){
                dp[i][j]=dp[i-1][j];
            }
            //进行dp
            for(ll start=1;start<=5;start++){
                ll cur=start;
                ll v=0;
                for(ll j=0;j<m;j++){
                    if(s[j]==word[cur]){
                        if(cur==5){
                            v+=10;
                            cur=1;
                        }else {
                            cur++;
                        }
                    }
                }
                //if(start==1) printf("!!%lld!!\n",v);
                v-=cost[i];
                ll lst=start-1;
                if(lst==0) lst=5;
                cur--;
                if(cur==0) cur=5;
                dp[i][cur]=max(dp[i][cur],dp[i-1][lst]+v);
                //这里发现,如果没有找到start对应的字符,这样更新也是合理的
            }
        }
        /*
        //输出dp table进行调试
        for(ll i=1;i<=n;i++){
            for(ll k=1;k<=5;k++){
              //printf("dp[%lld][%lld]=%lld\n",i,k,dp[i][k]);
            }
        }*/
        ll ans=-inf;
        for(ll i=1;i<=5;i++){
            ans=max(ans,dp[n][i]);
        }
        cout<<ans<<"\n";
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值