题解 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;
}
本作品由happyZYM采用知识共享 署名-非商业性使用-相同方式共享 4.0 (CC BY-NC-SA 4.0) 国际许可协议(镜像(简单版)镜像(完整版))进行许可。
转载请注明出处:https://www.cnblogs.com/happyZYM/p/11622914.html (近乎)全文转载而非引用的请在文首添加出处链接。