/*
區間dp,為什麼要升維?
因為若用dp[l][r]表示消去dp[l][r]的最大的分,那麼顯然狀態轉移方程dp[l][r]=max{dp[l+1][k-1]+(len[l]+len[k])^2+len[k+1][r]}
可是這樣是直接消去l和k兩個快的,有一種情況是在k.r兩個塊之間還有個同色塊,那麼這種情況就考慮不到了
所以我們要考慮是否能先不直接消去l,k合併的塊,而是將其保留下來,之後列舉到k,r區域的塊時再一同合併進行考慮
所以再加一維來記錄l,k合併後的資訊 為了方便,再加一維來表示r後面的同色塊情況 dp[l][r][q]表示區間消去區間[l,r]並且區間右側有長度為q的和塊r顏色相同的塊,所得到的分數
此時有兩種情況
1:r和len合併,直接消去
dp[l][r][q]=dp[l][r-1][0]+(len[r]+q)^2;
2: r和len合併,並且和[l,r-1]中的k塊合併
dp[l][r][q]=max{dp[l][k][len[j]+q]+dp[k+1][r-1][0]}
兩種情況取最大值即可
初始化狀態dp[i][i]=1,目標狀態dp[1][m][0]
*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 220
int n,m,a[maxn],color[maxn],len[maxn],dp[maxn][maxn][maxn]; int dfs(int l,int r,int q){
if(dp[l][r][q]!=-)return dp[l][r][q];
if(l==r) return dp[l][r][q]=(len[r]+q)*(len[r]+q);
int Max=(len[r]+q)*(len[r]+q);
Max+=dfs(l,r-,);//第一種情況
for(int k=l;k<r;k++)
if(color[k]==color[r]) Max=max(Max,dfs(l,k,q+len[r])+dfs(k+,r-,));
//printf("%d %d %d %d\n",l,r,q,Max);
return dp[l][r][q]=Max;
}
int main(){
int t;
cin>>t;
for(int tt=;tt<=t;tt++){
printf("Case %d: ",tt); cin>>n;
for(int i=;i<=n;i++)cin>>a[i];
int last=a[];
m=,color[]=a[],len[]=;
for(int i=;i<=n;i++){//把顏色小塊合併成大塊
if(a[i]==last)len[m]++;
else {
last=a[i];color[++m]=a[i];len[m]=;
}
} memset(dp,-,sizeof dp);
dfs(,m,);
printf("%d\n",dp[][m][]);
}
}

cf的題

/*
dp[l][r][q]表示消去區間[l,r]+q的最大值
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 200
ll n,dp[maxn][maxn][maxn],c[maxn],len[maxn];
char s[maxn];
ll dfs(int l,int r,int q){
if(l>r)return ;
if(l==r)return len[+q];
if(~dp[l][r][q])return dp[l][r][q]; ll Max=dfs(l,r-,)+len[+q];
for(int k=l;k<r;k++)
if(s[r]==s[k])Max=max(Max,dfs(l,k,q+)+dfs(k+,r-,));
//printf("%d %d %d %d\n",l,r,q,Max);
return dp[l][r][q]=Max;
}
int main(){
cin>>n;
scanf("%s",s);
for(int i=;i<=n;i++)cin>>len[i];
memset(dp,-,sizeof dp);
printf("%lld\n",dfs(,n-,));
}