Starlight_Glimmer
2020-11-22 21:45:05
My Blog:https://www.cnblogs.com/lyttt/p/14021670.html
题目链接
显然这道题重点在于消掉一些块之后会产生一些新的连续的块可以一起消,这个不能想到别的什么做法,可以考虑区间
不过对于一整坨,你要么把他们全部一起消掉,要么就留着等更多的过来一起消掉,你总不可能把一坨分批消掉吧,明显
所以刚开始可以把相同颜色的一段块看成一点(不过我太懒了,最后没有这么写,当然,这不是重点,用
用惯常的思路进行考虑,设
单独消除的状态是很好转移的:
但如果是保留这一段,然后等着和其它的方块一起消呢?我们发现这个状态难以转移,因为这个区间的得分还与这个区间以外的前面消方块的状态息息相关。如果记录下前面消了哪些,还有哪些,在什么位置之类的相关信息,状态数无疑是巨大的。
类比于之前小球的做法,在之前预先计算得分,我们发现这个似乎也行不通,因为这个得分不再是简单的线性关系,而是二次函数,过去的贡献与现在消去的长度有关。
返回去想到这道题重点在于消掉一些块之后会产生一些新的连续的块可以一起消,对于保留的情况,我们是把
对于状态
为啥只需要记录
假设
所以:对于状态
最后得到转移方程:
(好久没写这么长的题解了,好家伙,写了一节课)
参考:《对一类动态规划问题的研究》 徐源盛
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 205
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f*x;
}
int n,a[N],pre[N],pos[N];
LL f[N][N][N];//f[i][j][k]表示消去[i,j]区间 且j右边还有k个和j颜色相同的块
/*
主体部分是连续的[i,j]区间 没有动过
然后j右边有颜色相同的k块紧接在j后面 他们之间的方块已经消完
*/
void Init()
{
memset(f,0,sizeof(f));
memset(pre,0,sizeof(pre));
memset(pos,0,sizeof(pos));
}
LL dp(int i,int j,int k)
{
if(i>j) return 0;
if(f[i][j][k]) return f[i][j][k];
LL res=dp(i,j-1,0)+(k+1)*(k+1);//和右边一整坨一起消掉
int l=pre[j];
while(l>=i)
{
res=max(res,dp(i,l,k+1)+dp(l+1,j-1,0));//拿掉[l+1,j-1] 相同颜色的又拼到一起了
l=pre[l];
}
return f[i][j][k]=res;
}
int main()
{
int T=rd();
for(int cas=1;cas<=T;cas++)
{
Init();
n=rd();
for(int i=1;i<=n;i++)
{
a[i]=rd();
pre[i]=pos[a[i]],pos[a[i]]=i;
}
printf("Case %d: %lld\n",cas,dp(1,n,0));
}
return 0;
}