题目大意:牛在第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;
}