题目分析
听说是个 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;
}
0 条评论