7月9日二更:突然发现一个神奇的事情,把代码写成这样会更快(110ms)(就是用临时变量ans代替f[l][r][elen]),可见全局变量的寻址时间还是比较长的
开O3优化,实际效果几乎等于0
int dp(int l, int r,int elen) {
if (l == r)return (len[r] + elen) * (len[r] + elen);
if (f[l][r][elen])return f[l][r][elen];
int ans = dp(l, r - 1, 0) + (elen + len[r])*(elen + len[r]);
for (int i = l; i <= r - 2; i++)
if (col[i] == col[r]) {
ans = max(ans, dp(l, i, elen + len[r])
+ dp(i + 1, r - 1, 0));
}
return f[l][r][elen] = ans;
}
网上的代码基本上都一样,都是从北大的算法课过来的。
题意:给你一个涂有颜色的方块的序列,每次你可以删除一些相同颜色并且相邻的颜色块,并获得删除数目平方的收益,现在给你一个颜色块序列,问收益最大值是多少?
Sample Input
2 //表示t
9 //n
1 2 2 2 2 3 3 3 1 //表示颜色
1
1
Sample Output
Case 1: 29
Case 2: 1
思路:
第一步先把方块合并成连续且颜色一样的大块,方便处理。
对于L到R区间的最优解,一般人可能会想,要么分成三截,先删除中间一节,然后期望两边的颜色一样,在删除两边的;或者直接从中间切成两截,然后求值。比较两种情况的最大值。
其实这种思路是有问题的,比如1 2 1 1 1 2 1 ,你可以分成五截,1 3 5截颜色一样,你可以先删除2 4 截,然后1 3 5 一起删,明显比上一种想法更优。
但你不可能枚举每中分截情况,所以你可以考虑先把右边的留着不删,假设dp(l,r,e)表示l,r区间的最大值,且之前还有e个方块没删,e的颜色和第r个方块颜色一样。
你有两种方案,第一种是把r和e一起删了,然后去计算dp(l,r-1,0);第二种是中间有个方块k和r同色,那么期望k和r+e合并在删,就是dp(l, k, e + len[r])+ dp(k+ 1, r - 1, 0)。dp(l, k, e + len[r])表示l到k区间的最优解,希望e+len[r]+len[k]可以继续往下合并,dp(k+ 1, r - 1, 0)就是k+1到r-1的最优解。
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#include <stack>
#define INF 0x3f3f3f3f
#define IMAX 2147483646
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int
using namespace std;
int t, n, a[222],f[222][222][222],len[222],col[222],tot;
int dp(int l, int r,int elen) {
if (l == r)return (len[r] + elen) * (len[r] + elen);
if (f[l][r][elen])return f[l][r][elen];
f[l][r][elen] = dp(l, r - 1, 0) + (elen + len[r])*(elen + len[r]);
for (int i = l; i <= r - 2; i++)
if (col[i] == col[r]) {
f[l][r][elen] = max(f[l][r][elen], dp(l, i, elen + len[r])
+ dp(i + 1, r - 1, 0));
}
return f[l][r][elen];
}
int main() {
scanf("%d", &t);
for (int c = 1; c <= t; c++) {
scanf("%d", &n);
tot = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
if (i == 1 || a[i] != a[i - 1]) {
len[++tot] = 1;
col[tot] = a[i];
}
else
len[tot]++;
}
memset(f, 0, sizeof(f));
printf("Case %d: %d\n", c, dp(1, tot,0));
}
return 0;
}