[HAOI2016]字符合并

Luogu3736

5cd1836b5b165.png

很容易想到直接DP,关键是枚举顺序。
\(1.\)设后一段构成最后一个点,前一段构成前面的点,那么能得到\(1\)个点的数量要求 : \(1,k,2k-1...\)相差\(k-1\),所以能够剩下的位数是在模\(K-1\)意义下的
\(2.\)因为实质是从里面转移到外面,注意循环的正逆顺序 : \(mid\)\(j\)小 , 正序枚举\(j\) ; \(mid\)\(i\)大 , 倒序枚举\(i\)

具体细节见代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
#define int long long
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

int f[305][305][305],g[2],c[305],w[305],a[305];
int n,K,ans=-INF;

signed main(){
    n=read(),K=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=0;i<(1<<K);i++) c[i]=read(),w[i]=read();
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++)
            for(int k=0;k<(1<<K);k++)
                f[i][j][k]=-INF;
    }
    for(int i=n;i>=1;i--)
        for(int j=i;j<=n;j++){
            //注意循环的正逆顺序:mid比j小,正序枚举j;mid比i大,倒序枚举i
            if(i==j){f[i][j][a[i]]=0;continue;}
            int len=j-i;//前一段得到的长度
            len%=K-1; if(!len) len=K-1;
            for(int mid=j;mid>i;mid-=K-1)//能够得到1位的位数相差K-1
                for(int op=0;op<(1<<len);op++){//更新产生的状态
                    f[i][j][op<<1]=max(f[i][j][op<<1],f[i][mid-1][op]+f[mid][j][0]);//直接转移
                    f[i][j][op<<1|1]=max(f[i][j][op<<1|1],f[i][mid-1][op]+f[mid][j][1]);
                }
            }
            if(len==K-1){//加上一次操作的分数要单独讨论
                g[0]=g[1]=-INF;
                for(int op=0;op<(1<<K);op++)//这个地方是K位
                    g[c[op]]=max(g[c[op]],f[i][j][op]+w[op]);
                f[i][j][0]=g[0],f[i][j][1]=g[1];//用临时数组更新
            }
        }
    for(int op=0;op<(1<<K);op++)
        ans=max(ans,f[1][n][op]);
    printf("%lld\n",ans);
}

转载于:https://www.cnblogs.com/lizehon/p/10828127.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值