Description
在某个星球上,一天由N小时构成。我们称0-1点为第一个小时,1-2点为第二个小时,以此类推。
在第i个小时睡觉能恢复Ui点体力。在这座星球上住着一头牛,它每天要休息B个小时,它休息的
这B个小时可以不连续,可以分成若干段,但是在每一段的第一个小时不能恢复体力,从第二个小
时开始才可以恢复体力。 为了身体健康,这头牛希望遵循生物钟,每天采用相同的睡觉计划。另
外,因为时间是连续的,每天的第N个小时与下一天的第一个小时是相连的,这头牛只需要在N个小
时内休息B个小时就够了。 请你给这头牛安排一个任务计划,使得它每天恢复的体力最多。
N<=3830 0<Ui<200000
Sol
对于环的处理我们往往是复制一倍求区间解
但这道题复杂度上限为O(n2),不能这样,而且区间dp也不适合它,线性dp更适合
观察问题,发现可以分为两种情况:
- 第n个小时和第1个小时连续休息
- 第n个小时和第1个小时不连续休息
该时刻是否休息是一个关键,于是我们有了状态:
f[i][j][0/1]表示第1~i小时一共休息j小时并且第i小时不休息(0)/休息(1)所能获得最大体力
转移方程: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][1]+U[i],f[i-1][j-1][0]) (j>0)
对于两种情况,我们分别令dp初值为
- f[1][1][1]=U[1]
- f[1][1][0]=0,f[1][0][0]=0
其他均为负无穷(因为不是前提)
目标为
- max(f[n][b][1],f[n][b][0])
- f[n][b][1]
Code
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int N=3831; int f[N][N][2],d[N],n,b,T; void dp() { for(int i=2;i<=n;i++) for(int j=0;j<=b;j++) { f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]); if(j) f[i][j][1]=max(f[i-1][j-1][1]+d[i],f[i-1][j-1][0]); } } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&b); for(int i=1;i<=n;i++) scanf("%d",&d[i]); memset(f,0x80,sizeof(f)); f[1][1][1]=f[1][0][0]=0; dp(); int ans=max(f[n][b][0],f[n][b][1]); memset(f,0x80,sizeof(f)); f[1][1][1]=d[1]; dp(); printf("%d\n",max(ans,f[n][b][1])); } return 0; }