POJ - 2228 Naptime 环形结构上的dp 带注释代码

题目: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;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值