BZOJ4565: [Haoi2016]字符合并
区间Dp·状压Dp
题解:
又一道神题,蒟蒻orz
由于合并的获利为正,所以一定会合并到不能合并为止。我们发现一个长度len合并完的长度是已知的,length[i]=(i<m?i:length[i−m+1])
设f[i][j][s]表示区间[i,j]最后变成s的最大获利。
枚举一个mid,我们让右区间提供s的最后一位,左区间提供剩下的。为了让右边恰好提供一位,我们实际上应该跳着枚举mid.
那么不难写出方程:
f[i][j][s]=max(f[i][mid][s>>1]+f[mid+1][j][s&1])
注意,对于length=1的,特殊处理一下,加上获利。可以先当length=m来算,然后g[c[s]]=max(f[i][j][s]+w[s]).
边界就是f[i][i][a[i]]=1,f[i][i][a[i]xor1]=−INF
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 305;
const LL INF = 1e17;
int n,m;
int a[N],c[N],w[N],length[N];
LL f[N][N][N];
int main(){
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%1d",a+i);
f[i][i][a[i]]=0;
f[i][i][a[i]^1]=-INF;
}
for(int i=0;i<(1<<m);i++)
scanf("%d%d",c+i,w+i);
for(int i=1;i<m;i++) length[i]=i;
for(int i=m;i<=n;i++) length[i]=length[i-m+1];
for(int len=2;len<=n;len++){
int full=(1<<(length[len]==1?m:length[len]))-1;
for(int i=1,j=len;j<=n;i++,j++){
for(int s=0;s<=full;s++){
f[i][j][s]=-INF;
for(int mid=j-1;mid>=i;mid-=m-1){
f[i][j][s]=max(f[i][j][s],f[i][mid][s>>1]+f[mid+1][j][s&1]);
}
}
if(length[len]==1){
LL g[2]={-INF,-INF};
for(int s=0;s<=full;s++){
g[c[s]]=max(g[c[s]],f[i][j][s]+w[s]);
}
f[i][j][0]=g[0];
f[i][j][1]=g[1];
}
}
}
int full=(1<<length[n])-1;
LL ans=-INF;
for(int s=0;s<=full;s++){
ans=max(ans,f[1][n][s]);
}
printf("%lld\n",ans);
}