1109: 方块消除 blocks

时间限制: 1 Sec 内存限制: 128 MB O2
提交: 127 解决: 59
[提交][状态][博客][加入收藏]
题目描述
Jimmy最近迷上了一款叫做方块消除的游戏。 游戏规则如下:N个带颜色方格排成一列,相同颜色的方块连成一个区域(如果两个相邻的方块颜色相同,则这两个方块属于同一个区域).。游戏时,你可以任选一个区域消去。设这个区域包含的方块数为x,则将得到x^2的分值。方块消去之后,右边的方格将向左移动。

虽然游戏很简单,但是要得到高分也不是很容易.Jimmy希望你帮助他找出最高可以得到多少分。

输入
输入包含多组随机的测试数据。

第一行包含一个整数T,表示数据组数。

每组数据的第一行一个整数N,表示方块个数。第二行有N个整数,表示每个方块的颜色Ci。

输出
每组数据输出一个整数,即最高得分。

样例输入
2
9
1 2 2 2 2 3 3 3 1
1
1
样例输出
29
1
提示
【样例解释】

第一组数据的解释:

如图,依次消去蓝,白,黑区域,将得到42+32+2^2=29分,这是最高得分。

【数据范围】

1<=T<=15

10%的数据:0<=Ci<=N<=25

50%的数据:0<=Ci<=N<200

100%的数据:0<=Ci<=N<=200

来源
1020

题解:
因为答案由颜色相同的连续段长度贡献,故考虑对数据进行变形,即将相同颜色的连续一段缩为一点,记录下颜色和长度,这样方便计算。
原始想法:我们的任务是求1——n区间的最大得分,在进行一步操作后,这个区间变成一个更小的区间,但由于可以删除中间的一段,故得到的更小区间难以表示。也就是说,如果直接做区间DP,则难以转移。
而造成这样的主要原因在于若将原段分为两段做,则其中一段与另一段颜色相同的部分可能对答案造成更多贡献,即对于一个区间,在除这个区间外的部分进行一系列操作后,区间的左右端点两旁可能有新的颜色与之相同的方块,故在DP时要多记录左右端点两旁多的相同颜色方块数。而实际上,在区间转移时不必左右端点都同时转移,只需转移一端就可覆盖所有情况,故多的相同颜色方块数也只需记要转移的那一端即可。不难转移,细节详见代码。

代码:

#include
using namespace std;
int R(){
    int s=0; char c=getchar(); while(c<'0' || c>'9') c=getchar();
	while(c>='0' && c<='9') s=s*10+c-'0',c=getchar(); return s;
}
using namespace std;
const int N=202;
int T,c[N],l[N],lst[N],pre[N],s[N],f[N][N][N]; bool dn[N][N][N];
void work(int i,int j,int k){ 
	dn[i][j][k]=1; 
	if(k>s[j]) return; if(j==i-1){f[i][j][k]=0; return;}
	if(!dn[i][j-1][0]) work(i,j-1,0);
	f[i][j][k]=f[i][j-1][0]+(l[j]+k)*(l[j]+k); 
	for(int p=pre[j];p>=i;p=pre[p]){
		if(!dn[i][p][k+l[j]]) work(i,p,k+l[j]); if(!dn[p+1][j-1][0]) work(p+1,j-1,0);
		f[i][j][k]=max(f[i][j][k],f[i][p][k+l[j]]+f[p+1][j-1][0]);
	}
}
int main()
{
	cin>>T;
	while(T--){
		int n=R(),m=0; memset(dn,0,sizeof(dn)); memset(lst,0,sizeof(lst));
		for(int i=1;i<=n;i++){
		    int x=R(); if(x==c[m]) l[m]++; 
		    else {c[++m]=x; l[m]=1; pre[m]=lst[x]; lst[x]=m;}
		}
		memset(f,-0x3f,sizeof(f)); 
		for(int i=1;i<=m;i++){
		    s[i]=0; for(int j=i+1;j<=m;j++) if(c[j]==c[i]) s[i]+=l[j]; 
		}
		work(1,m,0); 
		cout<

你可能感兴趣的:(#,区间DP,区间,DP)