蓝书(算法竞赛进阶指南)刷题记录——POJ2888 Naptime(DP)

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

题目:POJ2888.
题目大意:给定一个长度为 n n 的环状数列,在这个环状数列上取 m m 个数,其中每一个极大连续段的开头没有贡献,其余均有贡献,求最大贡献和.
0 b n 3600 0\leq b\leq n\leq 3600 .

首先不考虑这是个环而是个序列,直接DP.设 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] 表示第 1 1 i i 个数中取了 j j 个数且第 i i 个数不选 / / 选的最大贡献和,容易列出方程:
f [ i ] [ j ] [ 0 ] = m a x ( f [ i 1 ] [ j ] [ 0 ] , f [ i 1 ] [ j ] [ 1 ] ) f [ i ] [ j ] [ 1 ] = m a x ( f [ i 1 ] [ j 1 ] [ 0 ] , f [ i 1 ] [ j 1 ] [ 1 ] + a [ i ] ) . f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1])\\ f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]).

初态 f [ 1 ] [ 0 ] [ 0 ] = f [ 1 ] [ 1 ] [ 1 ] = 0 f[1][0][0]=f[1][1][1]=0 ,答案 m a x ( f [ n ] [ m ] [ 0 ] , f [ n ] [ m ] [ 1 ] ) max(f[n][m][0],f[n][m][1]) .显然这个DP可以做到 O ( n 2 ) O(n^2) .

考虑如何把这个做法拓展到环上.

重新考虑一下我们漏掉了哪种情况,发现只漏掉了一种第 n n 个和第 1 1 个同时选的情况,这个情况下貌似只需要把初态改成 f [ 1 ] [ 1 ] [ 1 ] = a [ 1 ] f[1][1][1]=a[1] ,答案变为 f [ n ] [ m ] [ 1 ] f[n][m][1] 即可.

于是我们只需要跑两遍DP即可解决这个问题,时空复杂度 O ( n 2 ) O(n^2) .

然而这个做法在POJ上会爆空间,所以需要滚动数组优化一下空间复杂度.

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=3830,INF=(1<<30)-1;

int n,m,a[N+9],ans,dp[N+9][2];

void Pre_dp(){
  for (int j=0;j<=m;++j)
    for (int k=0;k<2;++k)
      dp[j][k]=-INF;
}

void Solve_dp(){
  for (int i=2;i<=n;++i)
    for (int j=m;j>=0;--j){
      dp[j][0]=max(dp[j][0],dp[j][1]);
      if (j>0) dp[j][1]=max(dp[j-1][0],dp[j-1][1]+a[i]);
	}
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  if (n<2) return;
  Pre_dp();
  dp[0][0]=dp[1][1]=0;
  Solve_dp();
  ans=max(dp[m][0],dp[m][1]);
  Pre_dp();
  dp[1][1]=a[1];
  Solve_dp();
  ans=max(ans,dp[m][1]);
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

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