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