版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/91632990
题目描述
题解
显然是一个区间DP,最直观的思路就是设置状态 为区间 的最高得分。
但是对于中间消除以后再消边上操作会十分困难,我们这里采用一种费用提前计算的方法:
我们设 表示在区间 消完以后,还要消在 右边颜色和 一样的 个的方案数。
我们可以用这幅图来诠释这个状态转移方程。
第一种情况,若
不能与
合并,直接将
的贡献加上r和右边的k合并以后的贡献。即:
第二种情况,将
中的两边合并,合并以后再用
和右边的某个
合并。此时需要保证
中合并的两端必须颜色相同。即:
对于枚举i,我们需要枚举每一个和r颜色相同的点,我们可以额外用一个
表示上一个和i颜色相同的点的位置。这样我们可以避免无用的枚举。
这道题的启示就是,我们当未来某一个状态还不确定的时候,适当的采用费用提前的手段,并增加一维状态来限制这一个答案。例如,这里的 就是一个假想的存在,其作用就是用于费用提前计算来方便我们统计答案。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 300;
int n;
int a[N], last[N], pre[N], f[N][N][N];
int dp(int l,int r,int k)
{
if (l > r) return 0;
if (f[l][r][k] > 0) return f[l][r][k];
if (a[r-1] ^ a[r]) f[l][r][k] = dp(l,r-1,0)+(1+k)*(1+k);//格子r单独和右边的格子消
for (int i=pre[r];i>=l;i=pre[i])//枚举每一个和颜色r相同的格子
f[l][r][k] = max(f[l][r][k],dp(l,i,k+1)+dp(i+1,r-1,0));//将i和「r和后面的k合并以后的格子」 合并
return f[l][r][k];
}
int main(void)
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i) {
scanf("%d",a+i);
pre[i] = last[a[i]];//记录「和位置i颜色相同的上一次出现」的格子的位置
last[a[i]] = i;
}
cout << dp(1,n,0) << endl;
return 0;
}