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