蓝书(算法竞赛进阶指南)刷题记录——POJ1390 Blocks(区间DP+记忆化搜索)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/90766227

题目:POJ1390.
题目大意:给定一个长度为 n n 的序列,每次可以删除权值相同连续一段且得分为长度的平方,求最大得分.
数据组数 15 \leq 15 , 1 n 200 1\leq n\leq 200 .

按照区间DP的套路,设 f [ l ] [ r ] f[l][r] 表示区间 [ l , r ] [l,r] 的答案,发现根本没办法转移.

考虑无法转移的原因是什么,我们发现在转移的时候,若把中间消掉了,还有一段可以并起来,但是这一段并起来的比原来增加的增量并不能直接算出来,现在我们要解决的是如何提前预知增量.

我们先把连续的同色段并称一个二元组 ( a i , c i ) (a_i,c_i) ,表示为 a i a_i 的颜色连续出现了 c i c_i 个.然后我们设 f [ l ] [ r ] [ k ] f[l][r][k] 表示区间 [ l , r ] [l,r] 中第 r r 个二元组变成 ( a r , c r + k ) (a_r,c_r+k) 后的答案,这个问题就变得容易了许多.

考虑如何转移,一种情况是 f [ l ] [ r ] [ k ] f[l][r][k] 中第 r r 个二元组可以不与前面任意一个合并,即 f [ l ] [ r 1 ] [ 0 ] + ( c r + k ) 2 f[l][r-1][0]+(c_r+k)^2 ;另一种是当满足 i [ l , r 1 ) i\in[l,r-1) a [ i ] = a [ r ] a[i]=a[r] 时,第 r r 个二元组与第 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 ] [ 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.

这个算法的时间复杂度为 O ( n 4 ) O(n^4) ,但其实有大量无用状态不用计算,所以用记忆化搜索实现后常数会变得非常小,实测可以通过本题.

代码如下:

#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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/90766227