题目:http://poj.org/problem?id=2228
翻译:https://vjudge.net/problem/POJ-2228#author=larryzhong
环形问题可以将环形切成线型
假如这个问题是线性问题,就很容易想到dp:
假如是从0小时开始,到N小时结束,我们可以:
dp[i][j][k] : i:此时是几点 j:此时在前i点使用了多少时间睡觉 k:k=0表示第i点睡觉, k=1表示第i点不睡觉
则这个dp表示的是恢复的最大体力,最后max(dp[n][b][0],dp[n][b][1])就是答案。
那么
val[i] : 第i个小时熟睡所恢复的精力
dp[i][j][1] = max(dp[i-1][j-1][1]+val[i],dp[i-1][j-1][0])
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])
初始化: dp[1][1][1]=0; dp[1][0][0]=0 , 其余初始化为-INF
但是这个问题是环型的,对于0点~1点,有:
1.这段时间清醒 2.这段时间刚刚入睡 3.这段时间已经睡熟
其中1,2两点说明0点之前的那个小时没有入睡,因此1和2都可以归为一类,可以将0小时这个点切开,相当于上面的问题。
而对于第3点,我们只需要再进行一次dp ,其中初始化dp[1][1][1]=val[i] 其余设为-INF
最后答案取最大值即可
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 4e3 + 7;
int dp[maxn][2]; //dp[前i个小时休息了j个小时][option 0:当前不睡 1:当前睡]
int num[maxn];
int main() {
int n, b;
cin >> n >> b;
for (int i = 1; i <= n; i++) scanf("%d", num + i);
memset(dp, -INF, sizeof(dp));
dp[1][1] = 0; //第一个小时必定没睡或者刚入睡
dp[0][0] = 0;
for (int i = 2; i <= n; i++) {
for (int j = min(i, b); j; j--) { //根据j的定义,j<=i
dp[j][0] = max(dp[j][0], dp[j][1]);
if (j >= 1) dp[j][1] = max(dp[j - 1][0], dp[j - 1][1] + num[i]);
}
}
int ans = max(dp[b][0], dp[b][1]);
memset(dp, -INF, sizeof(dp));
dp[1][1] = num[1];//第一个小时必定睡着了
for (int i = 2; i <= n; i++) {
for (int j = min(i, b); j; j--) {
dp[j][0] = max(dp[j][0], dp[j][1]);
if (j >= 1) dp[j][1] = max(dp[j - 1][0], dp[j - 1][1] + num[i]);
}
}
ans = max(ans, dp[b][1]); //前一天最后一个晚上一定入睡
cout << ans << endl;
}