题目分析
听说是个DP经典套路?
令 f ( i ) f(i) f(i)表示 i i i个一样的连在一起的元素,被消完的最大分数,一个完全背包可以搞定。
然后将连续的一段相同元素合成一个点,原序列变成了若干黑白交错的点,记点 i i i中的元素个数为 s z ( i ) sz(i) sz(i)。
令 g ( l , r , k ) g(l,r,k) g(l,r,k)表示现在要消完 [ l , r ] [l,r] [l,r]这一段点(和后面的 k k k个元素),点 r r r后面有 k k k个与 r r r点颜色相同的元素。那么 r r r可以选择跟前面的元素一起消,也可以不一起消。
不一起消: g ( l , r − 1 , 0 ) + f ( s z ( r ) + k ) g(l,r-1,0)+f(sz(r)+k) g(l,r−1,0)+f(sz(r)+k)
一起消:( i i i是 [ l , r − 1 ] [l,r-1] [l,r−1]中一个和 r r r颜色相同的点) g ( i + 1 , r − 1 , 0 ) + g ( l , i , k + s z ( r ) ) g(i+1,r-1,0)+g(l,i,k+sz(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;
}