POJ2228 Naptime 环形DP

题目大意:牛在第i个小时睡觉能够恢复U[i]点体力。睡觉时第一小时不恢复体力。一天的N小时连着下一天的1小时。求能够恢复体力的和的最大值。

定义DP[i][j][0]为前i个小时休息了j个小时,i小时不休息,DP[i][j][1]为休息。

如果不考虑N小时连着下一天的1小时,则有递归式:

DP[i][j][0] = max(DP[i - 1][j][0], DP[i - 1][j][1]);
if (j > 0)
    DP[i][j][1] = max(DP[i - 1][j - 1][0], DP[i - 1][j - 1][1] + U[i]);

初值为F[1][0][0](第一小时不睡觉)=DP[1][1][1](第一小时刚开始睡)=0,其余负无穷。终值为max(DP[N][B][0],DP[N][B][1])

剩下的只有第一小时在熟睡的情况了,只需把初值改为DP[1][1][1]=U[1](一小时在熟睡),终值改为DP[N][B][1](不管N时是在熟睡还是浅睡,必须是在睡觉)。

两种情况取最大值即可。

注意:

  • DP时要用滚动数组,否则会超内存
  • 仔细看看初值与终值。
  • 打完代码必须肉眼检查,像把i写成i-1的错误。
  • 共用的innerDP不要两个函数里分别写,有不便于修改等缺点。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_N = 4000, MAX_B = MAX_N;
int N, B;
int U[MAX_N];
int DP[2][MAX_B][2];

void innerDp()
{
	for (int i = 2; i <= N; i++)
	{
		memset(DP[i & 1], 0xcf, sizeof(DP[i & 1]));
		for (int j = 0; j <= i; j++)
		{
			DP[i & 1][j][0] = max(DP[i - 1 & 1][j][0], DP[i - 1 & 1][j][1]);
			if (j > 0)
				DP[i & 1][j][1] = max(DP[i - 1 & 1][j - 1][0], DP[i - 1 & 1][j - 1][1] + U[i]);
		}
	}
}

int DP1()
{
	memset(DP, 0xcf, sizeof(DP));
	DP[1][0][0] = DP[1][1][1] = 0;

	innerDp();
	return max(DP[N&1][B][0], DP[N&1][B][1]);
}


int DP2()
{
	memset(DP, 0xcf, sizeof(DP));
	DP[1][1][1] = U[1];

	innerDp();
	return DP[N&1][B][1];
}

int main()
{
#ifdef _DEBUG
	freopen("c:\noi\source\input.txt", "r", stdin);
#endif
	scanf("%d%d", &N, &B);
	for (int i = 1; i <= N; i++)
		scanf("%d", i + U);
	int a = DP1();
	//printf("%d
", a);
	int b = DP2();
	//printf("%d
", b);
	printf("%d
", max(a, b));
	return 0;
}

  

原文地址:https://www.cnblogs.com/headboy2002/p/8545354.html