题目: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
#include
#include
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;
}