【题目大意】
一天由$n$个小时构成,在第$i$个小时睡觉能够恢复$U_i$点体力。有一头牛要休息$b$个小时,可以不连续,但休息的第1个小时无法恢复体力。前一天的最后一个小时和第二天的第一个小时是连在一起的,求这头牛能恢复的体力最大值。
【思路解析】
首先这是一个环形DP问题,所以我们先简化问题,假设前一天的最后一个小时和第二天的第一个小时不是连在一起的,转化为线性问题来处理。设$f[i][j][1]$表示前$i$个小时休息了$j$个小时,并且第$i$个小时在休息的体力恢复最大值,$f[i][j][0]$则表示第$i$个小时不休息的体力恢复最大值。于是可以得到转移方程:
$$f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+u[i])$$
$$f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1])$$
初始值:$f[1][0][0]=f[1][1][1]=0$,其余为负无穷
目标:$max(f[n][b][0],f[n][b][1])$
然后我们考虑前一天的最后一个小时和第二天的第一个小时连在一起的情况,只需要把初始值改为$f[1][1][1]=U_1$,其余为负无穷,再跑一遍DP,然后答案为$f[n][b][1]$(为了保证第一个小时能恢复体力,前一天的最后一个小时必须休息)。
【代码实现】
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define rg register 5 #define go(i,a,b) for(rg int i=a;i<=b;i++) 6 #define back(i,a,b) for(rg int i=a;i>=b;i--) 7 #define mem(a) memset(a,128,sizeof(a)); 8 using namespace std; 9 const int N=3832; 10 const int INF=1e9+9; 11 int n,b,u[N],f[N][2],ans=-INF; 12 int main(){ 13 scanf("%d%d",&n,&b); 14 go(i,1,n) scanf("%d",&u[i]); 15 mem(f);f[0][0]=f[1][1]=0; 16 go(i,2,n) back(j,min(i,b),1){ 17 f[j][0]=max(f[j][0],f[j][1]); 18 f[j][1]=max(f[j-1][0],f[j-1][1]+u[i]); 19 } 20 ans=max(f[b][1],f[b][0]); 21 mem(f);f[1][1]=u[1]; 22 go(i,2,n) back(j,min(i,b),1){ 23 f[j][0]=max(f[j][0],f[j][1]); 24 f[j][1]=max(f[j-1][0],f[j-1][1]+u[i]); 25 } 26 ans=max(ans,f[b][1]); 27 printf("%d ",ans); 28 return 0; 29 }