问题描述
Some of you may have played a game called 'Blocks'. There are n blocks in a row, each box has a color. Here is an example: Gold, Silver, Silver, Silver, Silver, Bronze, Bronze, Bronze, Gold.
The corresponding picture will be as shown below:
If some adjacent boxes are all of the same color, and both the box to its left(if it exists) and its right(if it exists) are of some other color, we call it a 'box segment'. There are 4 box segments. That is: gold, silver, bronze, gold. There are 1, 4, 3, 1 box(es) in the segments respectively.
Every time, you can click a box, then the whole segment containing that box DISAPPEARS. If that segment is composed of k boxes, you will get kk points. for example, if you click on a silver box, the silver segment disappears, you got 44=16 points.
Now let's look at the picture below:
The first one is OPTIMAL.
Find the highest score you can get, given an initial state of this game.
输入格式
The first line contains the number of tests t(1<=t<=15). Each case contains two lines. The first line contains an integer n(1<=n<=200), the number of boxes. The second line contains n integers, representing the colors of each box. The integers are in the range 1~n.
输出格式
For each test case, print the case number and the highest possible score.
样例输入
2
9
1 2 2 2 2 3 3 3 1
1
1
样例输出
Case 1: 29
Case 2: 1
题意概括
给定一个序列,每次可以消去一个均为同种元素的区间,若区间长度为k,则将会获得k*k的分数。求最大可以获得的分数。
解析
显然这是一道区间动态规划的题目,可以用记忆化搜索的方式去实现。首先,我们可以用(l)和(r)表示区间的范围。但是,仅由此还不够进行状态转移,因为还需要一个变量来衡量连续区间的长度。我们的突破口是每一个状态子区间的独立性。
对于一个子区间([l,r]),想要消去这个区间就只有两个办法:一是消去与r相邻的所有相同颜色的方块,再消去区间([l,r-1]);二是找到在([l,r])中的某个位置(k)使(r)和(k)颜色相同,消去区间([k+1,r-1])使相同色块拼在一起后再一同消去,然后消去区间([l,k-1])。这样做的依据就是前面提到的独立性。对于任何一个区间,目标都只是消去该区间,而不用管其他的区间。区间内部则由区间的相互独立的子区间决定。这就说明了下面的方程中0的出现。
综上所述,设(f[l][r][k])表示在区间([l,r])右边与(r)同色的有(k)块时消去该区间(包括右边相同的部分)的最优解。我们可以得到如下状态转移方程:
其中(l<=i<=r,col[i]=col[r])。为了题目需要,我们还需要记录每个色块前面第一个和自己相同的颜色。这个可以借鉴图论中邻接表的思想来实现。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 202
using namespace std;
int t,n,i,a[N],f[N][N][N],nxt[N],head[N],cnt;
int dp(int l,int r,int k)
{
if(l>r) return 0;
if(f[l][r][k]!=0) return f[l][r][k];
f[l][r][k]=max(f[l][r][k],dp(l,r-1,0)+(k+1)*(k+1));
for(int i=nxt[r];i>=l;i=nxt[i]){
f[l][r][k]=max(f[l][r][k],dp(l,i,k+1)+dp(i+1,r-1,0));
}
return f[l][r][k];
}
int main()
{
cin>>t;
while(t--){
cnt++;
cin>>n;
memset(f,0,sizeof(f));
memset(head,0,sizeof(head));
memset(nxt,0,sizeof(nxt));
for(i=1;i<=n;i++){
cin>>a[i];
nxt[i]=head[a[i]];
head[a[i]]=i;
}
cout<<"Case "<<cnt<<": "<<dp(1,n,0)<<endl;
}
}