https://www.spoj.com/problems/NAPTIME/en/
题目
一天有n小时,有头牛,每n小时只能在床上呆B小时(不一定连续),在床上的第一小时还不能睡着,要第二小时开始才睡着,如果在第i小时的时候是睡着的,她就可以获得$U_i$点精力,先选择连续的n小时,然后在这n小时中选择不一定连续的B小时使她获得的精力最大,求最大值。
3 <= N <= 3,830
题解
第一想法:拆成环,然后dp[a][b][c][2]表示从第a小时开始,现在是b小时,在床上呆了c小时,这时是否在床上,然后就肯定超时了
其实有另外的做法
直接在一天内dp,区别只是“最后一小时处于睡眠状态并且第一小时获得了精力”的真假,那么可以进行两次背包
dp[i][j][0/1]表示现在是i,在床上呆了c小时,这时是否在床上
那么很容易写出转移
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cassert> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; #define MAXN 3837 int u[MAXN]; int dp[2][MAXN][2]; //now B int main() { int t; scanf("%d", &t); while(0<t--) { int n,b; scanf("%d%d", &n, &b); REP(i,0,n) { scanf("%d", &u[i]); } memset(dp,-0x3f,sizeof dp); dp[0][0][0]=0; dp[0][1][1]=0; int now=0; REP(i,1,n) { now^=1; REPE(j,0,b) { if(j) dp[now][j][1]=max(dp[now^1][j-1][0],dp[now^1][j-1][1]+u[i]); dp[now][j][0]=max(dp[now^1][j][0],dp[now^1][j][1]); } } int ans=max(dp[now][b][0],dp[now][b][1]); now=0; memset(dp,-0x3f,sizeof dp); dp[0][1][1]=u[0]; REP(i,1,n) { now^=1; REPE(j,0,b) { if(j) dp[now][j][1]=max(dp[now^1][j-1][0],dp[now^1][j-1][1]+u[i]); dp[now][j][0]=max(dp[now^1][j][0],dp[now^1][j][1]); } } ans = max(ans, dp[now][b][1]); printf("%d ", ans); } }