[BZOJ4565][HAOI2016]字符合并(区间状压DP)
https://blog.csdn.net/xyz32768/article/details/81591955
首先区间DP和状压DP是比较明显的,设f[L][R][S]为将[L,R]这一段独立操作最终得到的字符序列为S的最大收益。其中S的位数为(R-L)%(k-1)+1。
枚举R第一次参与的操作的左端点mid与这次操作得到的数转移,若当前长度为k则要加上当前操作收益。
注意这个mid和R的距离一定是k-1的倍数,这样总状态和转移就很少,于是可以$O(n^32^8)$通过。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=310; 9 const ll inf=1e14; 10 int n,k,a[N],c[N],w[N]; 11 ll ans,f[N][N][N]; 12 char s[N]; 13 14 int main(){ 15 freopen("bzoj4565.in","r",stdin); 16 freopen("bzoj4565.out","w",stdout); 17 scanf("%d%d%s",&n,&k,s+1); 18 rep(i,1,n) a[i]=s[i]-'0'; 19 for (int i=0; i<(1<<k); i++) scanf("%d%d",&c[i],&w[i]); 20 rep(i,0,n) rep(j,0,n) rep(z,0,(1<<k)) f[i][j][z]=-inf; 21 rep(i,1,n) f[i][i][a[i]]=0; 22 rep(l,2,n) rep(i,1,n-l+1){ 23 int j=i+l-1,len=j-i; 24 while (len>k-1) len-=k-1; 25 for (int mid=j; mid>i; mid-=k-1) 26 for (int z=0; z<(1<<len); z++){ 27 f[i][j][z<<1]=max(f[i][j][z<<1],f[i][mid-1][z]+f[mid][j][0]); 28 f[i][j][(z<<1)|1]=max(f[i][j][(z<<1)|1],f[i][mid-1][z]+f[mid][j][1]); 29 } 30 if (len==k-1){ 31 ll g[2]; g[0]=g[1]=-inf; 32 for (int z=0; z<(1<<k); z++) g[c[z]]=max(g[c[z]],f[i][j][z]+w[z]); 33 f[i][j][0]=g[0]; f[i][j][1]=g[1]; 34 } 35 } 36 for (int i=0; i<(1<<k); i++) ans=max(ans,f[1][n][i]); 37 printf("%lld\n",ans); 38 return 0; 39 }