(补题 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;
}