2018.10.25 bzoj4565: [Haoi2016]字符合并(区间dp+状压)

183 篇文章 0 订阅
22 篇文章 0 订阅

传送门
当看到那个 k ≤ 8 k\le 8 k8的时候就知道需要状压了。
状态定义: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示区间 [ i , j ] [i,j] [i,j]处理完之后的状态为 k k k时的最大贡献。
由于每次合并会减少 K − 1 K-1 K1个,因此 0 &lt; k ≤ K − 1 0&lt;k\le K-1 0<kK1
然后转移的时候不用一个一个转移。
两次转移的断点的距离需要保证是 k − 1 k-1 k1,因为这样子肯定不必之前距离不为 k − 1 k-1 k1时更优。
注意处理特殊情况(整个区间刚好可以被消玩)
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,up,s[305],sta[260];
ll f[305][305][260],w[260],ans=0;
char S[305];
inline ll read(){
    ll ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int main(){
    n=read(),k=read(),up=1<<k,memset(f,-1,sizeof(f));
    scanf("%s",S+1);
    for(int i=1;i<=n;++i)s[i]=S[i]-'0';
    for(int i=0;i<up;++i)sta[i]=read(),w[i]=read();
    for(int i=1;i<=n;++i)f[i][i][s[i]]=0; 
    for(int Len=1;Len<n;++Len)
        for(int l=1;l+Len<=n;++l){
            int r=l+Len,len=Len;
            while(len>k-1)len-=k-1;
            for(int m=r;m>l;m-=k-1)for(int stat=0;stat<(1<<len);++stat)
                if(~f[l][m-1][stat]){
                    if(~f[m][r][0])f[l][r][stat<<1]=max(f[l][r][stat<<1],f[l][m-1][stat]+f[m][r][0]);
                    if(~f[m][r][1])f[l][r][stat<<1|1]=max(f[l][r][stat<<1|1],f[l][m-1][stat]+f[m][r][1]);                       
                }
            if(len==k-1){
            	ll tmp[2];
            	tmp[0]=tmp[1]=-1;
            	for(int stat=0;stat<up;++stat)if(~f[l][r][stat])tmp[sta[stat]]=max(tmp[sta[stat]],f[l][r][stat]+w[stat]);
            	f[l][r][0]=tmp[0],f[l][r][1]=tmp[1];
			}
        }
    for(int i=0;i<up;++i)ans=max(ans,f[1][n][i]);
    cout<<ans;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值