题目
n个不同颜色的方块排成一行,每次消去一段长度为x的连续相同颜色方块(这一段是指直到这种颜色的边界为止),可以得到 积分,问最多可以得到多少积分
分析
这一题是区间动态规划,但是截然不同的是,如果按照dp[i,j]从i到j连续的一段来表示状态,那么无法表示XAXBXCX这种的最优解,因为它是消除了内部的分段A,B,C之后,还要把剩下的重新拼起来,而以前见到的都只有划分,没有重新拼接这一个部分
这道题的状态和转移方程非常巧妙,让我想估计一天都想不出,关键是给状态增加一个维度k表示后缀,得到的状态是dp[i][j][k]表示从方块i到方块j这一段,后面还有k个和方块j颜色相同的方块时的最大得分
首先对于右端的方块来说,只看消去时的结果,他要么是被独自消去的,要么是两端隔开的被连在了一起之后消除,所以决策就有两个
- 消除最右端的那一段方块(连带着后缀一起),
- 消除中间的一段, ,按照这个要求可以找出很多个q,枚举可能的q, ,这时状态转移方程中分为两部分,一个是被处理的中间部分dp(q+1, p-1, 0),另一个是剩下的dp(i, q, j-p+k+1),可以看到[p,j]变成了后缀部分
状态有 个,决策有O(n)个,时间复杂度 ,但实际使用的状态数量比较少,所以时间消耗不高
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=205;
int t,kase,n,color[maxn],dp[maxn][maxn][maxn];
int DP(int i,int j,int k){
if(i>j) return 0;
int &t=dp[i][j][k];
if(t>=0) return t;
int p=j-1;
while(p>=i && color[p]==color[j]) p--;
++p;
t=DP(i,p-1,0)+(j-p+1+k)*(j-p+1+k);
for(int q=i;q<p;++q){
if(color[q]==color[j] && color[q+1]!=color[j]){
t=max(t,DP(q+1,p-1,0)+DP(i,q,j-p+1+k));
}
}
return t;
}
int main(void){
cin>>t;
kase=1;
while(kase<=t){
cin>>n;
for(int i=0;i<n;++i) scanf("%d",&color[i]);
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
for(int k=0;k<n;++k)
dp[i][j][k]=-1;
}
}
int ans=DP(0,n-1,0);
printf("Case %d: %d\n",kase++,ans);
}
}