题目:POJ1390.
题目大意:给定一个长度为
n
n
n的序列,每次可以删除权值相同连续一段且得分为长度的平方,求最大得分.
数据组数
≤
15
\leq 15
≤15,
1
≤
n
≤
200
1\leq n\leq 200
1≤n≤200.
按照区间DP的套路,设 f [ l ] [ r ] f[l][r] f[l][r]表示区间 [ l , r ] [l,r] [l,r]的答案,发现根本没办法转移.
考虑无法转移的原因是什么,我们发现在转移的时候,若把中间消掉了,还有一段可以并起来,但是这一段并起来的比原来增加的增量并不能直接算出来,现在我们要解决的是如何提前预知增量.
我们先把连续的同色段并称一个二元组 ( a i , c i ) (a_i,c_i) (ai,ci),表示为 a i a_i ai的颜色连续出现了 c i c_i ci个.然后我们设 f [ l ] [ r ] [ k ] f[l][r][k] f[l][r][k]表示区间 [ l , r ] [l,r] [l,r]中第 r r r个二元组变成 ( a r , c r + k ) (a_r,c_r+k) (ar,cr+k)后的答案,这个问题就变得容易了许多.
考虑如何转移,一种情况是
f
[
l
]
[
r
]
[
k
]
f[l][r][k]
f[l][r][k]中第
r
r
r个二元组可以不与前面任意一个合并,即
f
[
l
]
[
r
−
1
]
[
0
]
+
(
c
r
+
k
)
2
f[l][r-1][0]+(c_r+k)^2
f[l][r−1][0]+(cr+k)2;另一种是当满足
i
∈
[
l
,
r
−
1
)
i\in[l,r-1)
i∈[l,r−1)且
a
[
i
]
=
a
[
r
]
a[i]=a[r]
a[i]=a[r]时,第
r
r
r个二元组与第
k
k
k个合并,即
f
[
l
]
[
i
]
[
c
r
+
k
]
+
f
[
m
i
d
+
1
]
[
r
−
1
]
[
0
]
f[l][i][c_r+k]+f[mid+1][r-1][0]
f[l][i][cr+k]+f[mid+1][r−1][0].即:
f
[
l
]
[
r
]
[
k
]
=
{
(
c
l
+
k
)
2
l
=
r
max
{
f
[
l
]
[
r
−
1
]
[
0
]
+
(
c
r
+
k
)
2
,
max
i
=
l
,
a
[
i
]
=
a
[
r
]
r
−
2
{
f
[
l
]
[
i
]
[
c
r
+
k
]
+
f
[
i
+
1
]
[
r
−
1
]
[
0
]
}
}
l
=
̸
r
f[l][r][k]=\left\{\begin{matrix} (c_l+k)^2&l=r\\ \max \left\{ f[l][r-1][0]+(c_r+k)^2,\max_{i=l,a[i]=a[r]}^{r-2} \left\{ f[l][i][c_r+k]+f[i+1][r-1][0] \right\} \right\}&l=\not{}r \end{matrix}\right.
f[l][r][k]={(cl+k)2max{f[l][r−1][0]+(cr+k)2,maxi=l,a[i]=a[r]r−2{f[l][i][cr+k]+f[i+1][r−1][0]}}l=rl≠r
这个算法的时间复杂度为 O ( n 4 ) O(n^4) O(n4),但其实有大量无用状态不用计算,所以用记忆化搜索实现后常数会变得非常小,实测可以通过本题.
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=200;
int n,a[N+9],cnt[N+9],dp[N+9][N+9][N+9],vis[N+9][N+9][N+9];
int sqr(int x){return x*x;}
int Dfs_dp(int L,int R,int i){
int &res=dp[L][R][i];
if (vis[L][R][i]) return res;
vis[L][R][i]=1;
if (L==R) return res=sqr(cnt[L]+i);
res=Dfs_dp(L,R-1,0)+sqr(cnt[R]+i);
for (int mid=L;mid<R-1;++mid)
if (a[mid]==a[R]) res=max(res,Dfs_dp(L,mid,cnt[R]+i)+Dfs_dp(mid+1,R-1,0));
return res;
}
Abigail into(){
scanf("%d",&n);
int ca=0,x;
for (int i=1;i<=n;++i){
scanf("%d",&x);
if (a[ca]==x) ++cnt[ca];
else a[++ca]=x,cnt[ca]=1;
}
n=ca;
}
Abigail work(){
for (int i=1;i<=n;++i)
for (int j=i;j<=n;++j)
for (int k=0;k<=n;++k)
vis[i][j][k]=0;
}
Abigail outo(int cas){
printf("Case %d: %d\n",cas,Dfs_dp(1,n,0));
}
int main(){
int T;
scanf("%d",&T);
for (int i=1;i<=T;++i){
into();
work();
outo(i);
}
return 0;
}