题目分析

听说是个 DP 经典套路?

令 $f(i)$表示 $i$个一样的连在一起的元素,被消完的最大分数,一个完全背包可以搞定。

然后将连续的一段相同元素合成一个点,原序列变成了若干黑白交错的点,记点 $i$中的元素个数为 $sz(i)$。

令 $g(l,r,k)$表示现在要消完 $[l,r]$这一段点(和后面的 $k$个元素),点 $r$后面有 $k$个与 $r$点颜色相同的元素。那么 $r$可以选择跟前面的元素一起消,也可以不一起消。

不一起消:$g(l,r-1,0)+f(sz(r)+k)$

一起消:($i$是 $[l,r-1]$中一个和 $r$颜色相同的点)$g(i+1,r-1,0)+g(l,i,k+sz(r))$

记忆化搜索,跑不满,加上 CF 评测姬快如闪电,可以过。

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
const int N=105;
int n,cnt;LL a[N],f[N],g[N][N][N];char S[N];
struct node{int sz,col;}b[N];

void prework() {
    for(RI i=1;i<=n;++i)
        for(RI j=i;j<=n;++j) f[j]=max(f[j],f[j-i]+a[i]);
    int now=0;
    for(RI i=1;i<=n;++i) {
        if(i==1||S[i]==S[i-1]) ++now;
        else ++cnt,b[cnt].sz=now,b[cnt].col=S[i-1]-'0',now=1;
    }
    ++cnt,b[cnt].sz=now,b[cnt].col=S[n]-'0';
}
LL DP(int l,int r,int k) {
    if(l==r) {return g[l][r][k]=f[b[l].sz+k];}
    if(g[l][r][k]!=-1) return g[l][r][k];
    LL re=DP(l,r-1,0)+f[b[r].sz+k];
    for(RI i=l;i<r-1;++i)
        if(b[i].col==b[r].col)
            re=max(re,DP(i+1,r-1,0)+DP(l,i,b[r].sz+k));
    return g[l][r][k]=re;
}
int main()
{
    scanf("%d",&n);
    scanf("%s",S+1);
    for(RI i=1;i<=n;++i) scanf("%lld",&a[i]);
    prework();
    memset(g,-1,sizeof(g));
    printf("%lld\n",DP(1,cnt,0));
    return 0;
}
分类: 文章

litble

苟...苟活者在淡红的血色中,会依稀看见微茫的希望

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注