【HAOI2016】字符合并
题面
https://www.luogu.org/problem/P3736
题解
首先,要是长度$>=k$,合并了肯定比不合并更优。
进一步的,区间$[l..r]$的字符,合并的长度是确定的,为$(r-l)\%(m-1)+1$。
可以设$f[l][r][v]$为区间$[l..r]$的字符,变成$v$的最大分数。不能舍去。
转移分两种,一种是拼,一种是变化。
处理拼的转移不需要枚举,直接钦定左区间构成了前$len-1$个字符,右区间构成了最后$1$个字符即可。
处理变化的情况正如我思考的一样,只需要在$len=1$的情况下枚举是哪一种$01$串构成的即可,然后再变成拼的情况。
说实话,这样钦定状态优化转移的技巧(类似于搜索中规定转移类型,规定转移顺序)是我所欠缺的,窝什么时候才能像$\mbox{Gloid}$爷一样思路顺畅的写出这样优美的$dp$啊。
#include<cstdio> #include<cstring> #include<iostream> #define ri register int #define N 305 #define LL long long using namespace std; int a[N]; int n,k; LL f[N][N][1<<8]; int to[1<<8],w[1<<8]; int main() { cin>>n>>k; for (ri i=1;i<=n;i++) cin>>a[i]; for (ri i=0;i<(1<<k);i++) cin>>to[i]>>w[i]; memset(f,-0x3f,sizeof(f)); for (ri i=1;i<=n;i++) f[i][i][a[i]]=0; for (ri len=1;len<n;len++) for (ri l=1;l+len<=n;l++) { int r=l+len; int L=len%(k-1)+1; for (ri d=r-1;d>=l;d-=(k-1)) { if (L==1) { for (ri i=0;i<(1<<k);i++) f[l][r][to[i]]=max(f[l][r][to[i]],f[l][d][i>>1]+f[d+1][r][i&1]+w[i]); } else { for (ri i=0;i<(1<<L);i++) f[l][r][i]=max(f[l][r][i],f[l][d][i>>1]+f[d+1][r][i&1]); } } } int ret=0; for (ri i=0;i<(1<<k);i++) if (f[1][n][i]>ret) ret=f[1][n][i]; cout<<ret<<endl; return 0; }