休息时间

算法分析:
环形问题,先考虑线性。这种一维上的dp,我们一般设状态 f [ i ] [ j ] f[i][j] f[i][j]表示规划了前 i i i个阶段,睡了 j j j个小时,且第i个小时是睡着的,恢复的最大体力值。则很容易写出如下方程:
f [ i ] [ j ] = m a x { f [ j ] [ s ] + d } f[i][j] = max\{f[j][s] + d\} f[i][j]=max{f[j][s]+d}

j是枚举量,s是枚举量,d是[s+1,i]这段的情况,有可能前半段睡着,后面醒了,或者前面醒了,后面又睡着,等复杂情况。这个状态不太可行。应该增加维度。

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示规划了前 i i i个阶段,睡了 j j j个小时,第i个小时的状态是k,恢复的最大体力值。其中k为1表示是睡着的,k为0表示是清醒的。这样状态就好转移多了。

1.假设休息时间不会跨越n和1,就是线性上的问题。
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 ] + u [ i ] ) \begin{aligned} 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] + u[i]) \end{aligned} f[i][j][0]f[i][j][1]=max(f[i1][j][0],f[i1][j][1])=max(f[i1][j1][0],f[i1][j1][1]+u[i])

边界:
f [ 1 ] [ 1 ] [ 0 ] = 0 ; f [ 1 ] [ 1 ] [ 1 ] = 0 ; f[1][1][0] = 0; f[1][1][1] = 0; f[1][1][0]=0;f[1][1][1]=0;,其余为负无穷。

2.以上没有考虑n和1跨越问题,即在第n小时是休息的,这样第1小时能够恢复体力值。只要强制定义为第1小时能恢复体力值,最后求 f [ n ] [ b ] [ 1 ] f[n][b][1] f[n][b][1]即可。其余一样。

边界:
f [ 1 ] [ 1 ] [ 1 ] = u [ 1 ] f[1][1][1] = u[1] f[1][1][1]=u[1],其余为负无穷。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll long long int 
int n, b, u[3835];
ll f[2][3805][2]; 
int main()  // 不滚动数组会MLE  
{
	scanf("%d%d", &n, &b);
	for (int i = 1; i <= n; ++i) scanf("%d", &u[i]);
	
	memset(f, 0xcf, sizeof(f));
	f[1&1][0][0] = 0; f[1&1][1][1] = 0;
	for (int i = 2; i <= n; ++i)
		for (int j = 0; j <= b; ++j)
		{
			f[i&1][j][0] = max(f[(i-1)&1][j][0], f[(i-1)&1][j][1]);
			if (j >= 1)	f[i&1][j][1] = max(f[(i-1)&1][j-1][0], f[(i-1)&1][j-1][1] + u[i]);
		}
	ll ans = 0;
	ans = max(f[n&1][b][0], f[n&1][b][1]);
	//
	memset(f, 0xcf, sizeof(f));
	f[1&1][1][1] = u[1];
	for (int i = 2; i <= n; ++i)
		for (int j = 0; j <= b; ++j)
		{
			f[i&1][j][0] = max(f[(i-1)&1][j][0], f[(i-1)&1][j][1]);
			if (j >= 1) f[i&1][j][1] = max(f[(i-1)&1][j-1][0], f[(i-1)&1][j-1][1] + u[i]);
		}
	ans = max(ans, f[n&1][b][1]);
	printf("%lld\n", ans);
	return 0;
}

反思与总结:
1.空间超了,要滚动。

2.环形问题除了常用的“破环成链”外,这种“线性+特殊情况”的模式也可以使用:两次dp。第一次线性做dp,第二次通过适当赋值和假设条件,保证计算出的状态等价于把断开的位置强制相连。

3.数据要用long long。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值