传送门

题意:

有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这 k 个字符确定。你需要求出你能获得的最大分数。

1n300,0ci1,1wi109,k8 1 ≤ n ≤ 300 , 0 ≤ c i ≤ 1 , 1 ≤ w i ≤ 10 9 , k ≤ 8

Solution:

姑且算是一个套路吧…

f[i][j][S] f [ i ] [ j ] [ S ] 表示把区间 [i,j] [ i , j ] 合并成S的状态所能得到的最大分数

可以发现我们将所有合并操作展开后不会有操作字符相交的情况出现

所以我们对于最终长度=len的区间,可以通过枚举mid,使得左边最终长度为len-1,右边最终长度为1

如果len=1,我们想把它当成len=k来做,然后枚举所有状态进行转移即可

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const long long minn=-1e18; 
long long f[310][310][1<<8];
int n,m;
int a[310],c[1<<8],v[1<<8];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%1d",&a[i]);
    for (int i=0;i<(1<<m);i++) scanf("%d%d",&c[i],&v[i]);
    for (int i=1;i<=n;i++) f[i][i][a[i]]=0,f[i][i][a[i]^1]=minn;
    for (int len=2;len<=n;len++)
        for (int l=1;l<=n-len+1;l++)
        {
            int r=l+len-1;
            int tot=len%(m-1);if (!tot) tot=m-1;if (tot==1) tot=m;
            for (int S=0;S<(1<<tot);S++)
            {
                f[l][r][S]=minn;
                for (int mid=r-1;mid>=l;mid-=(m-1))
                    f[l][r][S]=max(f[l][mid][S>>1]+f[mid+1][r][S&1],f[l][r][S]);
            }
            long long nw[2]={
  0,0};
            if (tot==m)
            {
                for (int S=0;S<(1<<tot);S++) nw[c[S]]=max(nw[c[S]],f[l][r][S]+v[S]);
                f[l][r][0]=nw[0];f[l][r][1]=nw[1];
            }
        }
    long long ans=0;
    for (int S=0;S<(1<<m);S++) ans=max(f[1][n][S],ans);
    printf("%lld",ans);
}