POJ
算法:区间 D P DP DP
状态:
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示消去
i
−
j
i-j
i−j这段区间,
k
k
k是右边可以利用的数(就是留下来准备合在一起的数)
每次消去这段数有两种方法:
- 直接消去这个数
- 留下这个数,枚举 i i i,消掉 i − k i-k i−k这段,然后和 i i i合到一起再消掉
转移方程:
直接消去的方式:
f[l][r][x]=dfs(l,r-1,0)+num*num;//直接消去右边
留下这个数和其他数合并后消去的方式:
if(co[i]==co[r])
f[l][r][x]=max(f[l][r][x],dfs(i+1,r-1,0)+dfs(l,i,num));//区间dp,先留着r,消掉i+1到r这段,在消掉l到i这段
注意:
- 由于状态是 O ( n 3 ) O(n^3) O(n3)的,转移是 O ( n ) O(n) O(n)的,总复杂度为 O ( n 4 ) O(n^4) O(n4)
- 预处理读数时可以先把相同的合在一起,区间长度减小了
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define rep(i,a,b) for(register int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e3+10;
int n;
int f[210][210][210];
int len[maxm],co[maxm];
template <class t> inline void read(t &x) {
x=0;char ch=getchar();int f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
x*=f;
}
int dfs(int l,int r,int x) {
int num=len[r]+x;
if(l>r) return 0;
if(l==r) return num*num;
if(f[l][r][x]!=-1) return f[l][r][x];
f[l][r][x]=dfs(l,r-1,0)+num*num;//直接消去右边
for(int i=l;i<=r-1;i++)
if(co[i]==co[r])
f[l][r][x]=max(f[l][r][x],dfs(i+1,r-1,0)+dfs(l,i,num));//区间dp,先留着右区间一块,消完中间再消右边
return f[l][r][x];
}
void readdata() {
int T;read(T);
int cnt=0;
while(T--) {
cnt++;
read(n);
memset(f,-1,sizeof(f));
int tot=1;
rep(i,1,n) {
int x;read(x);
if(i==1) {co[1]=x;len[1]=1;continue;}
if(i!=1 && x==co[tot]) {
co[i]=tot;
len[tot]++;
}
else {
tot++;
len[tot]=1;
co[tot]=x;
}
}
printf("Case %d: %d\n",cnt,dfs(1,tot,0));
}
}
int main() {
freopen("input.txt","r",stdin);
//freopen("fire.out","w",stdout);
readdata();
return 0;
}