(补题 POJ-2228)Naptime(环状DP)

题目描述

Goneril是一个非常贪睡的牛,一天有 (n) 个时间段,这牛希望一天可以休息 (B) 个时间段。如果牛在第 $i $ 时刻已经熟睡,他可以得到 (u_i) 的休息。但是如果他在(i) 时刚刚入睡,则不能得到休息。牛可以从前一天晚上睡到第二天。睡觉时间不一定连续。问如何安排睡觉时间,可以使牛得到的休息最大。

Sample Input

5 3
2
0
3
1
4

Sample Output

6

解题思路

环状dp,我们可以将它拆分成线性dp来解答,也就是拆成两个线性dp问题。

(dp[i][j][0])表示某天前i个小时睡了j个小时(但是第i个小时是醒着的)

(dp[i][j][1])表示某天前i个小时睡了j个小时(但是第i个小时是睡着的)

然后就出现了一个问题,那就是如果我们如果第一个时间段就是熟睡的如何去算呢?
我们可以强行让第一个小时就是熟睡的,即令(dp[1][1][1]=u_1)就可以了。

为了节省空间我们使用滚动数组代替两个dp数组;

代码样例

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

const int maxn=1e6;
const int inf=0x3f3f3f3f;
int dp[maxn][2],u[maxn];

int main(){
  int N,B;
  cin >> N >> B;
  for(int i=1; i <= N; i++){
    cin >> u[i];
  }
  memset(dp,-inf,sizeof(dp));
  dp[0][0]=dp[1][1]=0;
  for(int i=2; i <= N; i++){
    for(int j=B ;j > 0; j--){
      dp[j][0]=max(dp[j][0],dp[j][1]);
            dp[j][1]=max(dp[j-1][0],dp[j-1][1]+u[i]);
    }
  }
  int ans=-inf;
  ans=max(ans,max(dp[B][1],dp[B][0]));
  
  memset(dp,-inf,sizeof(dp));
  dp[1][1]=u[1];
  for(int i=2; i <= N; i++){
    for(int j=B ;j > 0; j--){
      dp[j][0]=max(dp[j][0],dp[j][1]);
            dp[j][1]=max(dp[j-1][0],dp[j-1][1]+u[i]);
    }
  }
  ans=max(ans,dp[B][1]);
  cout << ans << endl;
  return 0;
}
原文地址:https://www.cnblogs.com/cafu-chino/p/11985136.html