【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;
}
posted @ 2019-11-05 15:54  HellPix  阅读(197)  评论(0编辑  收藏  举报