题解 POJ1390 【Blocks】

题目链接:Link

Problem

Solution

首先不难想出一个二维的dp,然后你会发现你被这玩意儿弄跪了:

1
10
9 1 1 9 1 10 1 10 8 9 

二维dp的问题在于:已经积攒起来的方块只能随着分治的过程下放,没法积攒起来备用。
因此我们可以把维度增加一维,即: $ f(i,j,k)= $ 区间i到j,且在j的右侧已积累了k个与j号方块颜色相同的方块,此时的最优解
显然有两种选择。
第一种:先合并再说,此时 $ f(i,j,k)=f(i,j-1,0)+(k+c_j)^2 $
第二种:先对里面的动手,则必然存在一个p,其中 $ i \le p < j $ , 且 $ b_p = b_j $ ,先把p+1到j-1之间的合并掉,此时: $ f(i,j,k)=f(i,j,k+c_j)+f(p+1,j-1,0) $

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=205;
int T,kase,n,a[maxn],f[maxn][maxn][maxn],b[maxn],c[maxn],tot;
int dp(int i,int j,int k)
{
	int &res=f[i][j][k];
	if(res) return res;
	if(i==j) return res=(c[i]+k)*(c[i]+k);
	res=dp(i,j-1,0)+(k+c[j])*(k+c[j]);
	for(int p=i;p<j;p++)
		if(b[p]==b[j]) res=max(res,dp(i,p,k+c[j])+dp(p+1,j-1,0));
	return res;
}
int main()
{
	#ifdef local
	freopen("pro.in","r",stdin);
	#endif
	scanf("%d",&T);
	while(T-->0)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		tot=0;
		int lst=0;
		for(int i=1;i<=n;i++)
		{
			if(a[i]!=lst) { b[++tot]=a[i]; c[tot]=1; lst=a[i]; }
			else c[tot]++;
		}
		memset(f,0,sizeof(f));
		for(int i=1;i<=tot;i++) f[i][i][0]=c[i]*c[i];
		printf("Case %d: %d\n",++kase,dp(1,tot,0));
	}
	return 0;
}
posted @ 2019-10-04 19:24  happyZYM  阅读(135)  评论(0编辑  收藏  举报