与这一题而言,我们需要解决一些问题:
每一小时都接在前一小时后面,而当前能否获得体力值依赖于前一小时是否睡觉,这引出了一个环形的依赖关系问题
解决方案:将环斩成链,用区间DP处理链上的问题,
在1的基础下,又会有新的问题:
从哪里斩开?解决方案:从两天中间,也就是n时与0时的这个点上斩开
定义状态
dp[i][j] 在一天的前i个小时中睡了j小时
但我们会发现这项定义是有问题的,因为题目中提到,在单独的一段睡眠中,第一个小时不能恢复体力
所以我们要再pick一个维度,[0,1],代表dp[i]的第i个小时是否睡觉,那这样,我们就可以很好的兼顾这个情况
- 状态转移
$ dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j][1])$
当然,当第i小时未睡觉时,也没法get到这个时间段的体力值,只能从i - 1处睡j小时转移而来。
$ dp[i][j][1] = max(dp[i - 1][j - 1][0],dp[i - 1][j - 1][1] + u[i]); $
当第i个小时睡了觉时,
\({\color{red} {表达式} }\)
#include<bits/stdc++.h>
using namespace std;huodetilizhi
const int maxn = 3900;
const int maxm = 3900;
int T;
int dp[maxn][maxm][2] = {0};
int u[maxm],n,b,ans;
void Fdp(){
for(int i = 2;i <= n;i ++){
for(int j = 0;j <= min(i,b);j ++){
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j][1]);
if(j >= 1){
dp[i][j][1] = max(dp[i - 1][j - 1][0],dp[i - 1][j - 1][1] + u[i]);
}
}
}
}
void work(){
cin >> n >> b;
ans = -1e9;
if(b == 0){cout << "0" << endl;return;}
memset(dp,0xcf,sizeof(dp));
for(int i = 1;i <= n;i ++){
cin >> u[i];
}
dp[1][0][0] = 0;
dp[1][1][1] = 0;
Fdp();
ans = max(dp[n][b][0],dp[n][b][1]);
memset(dp,0xcf,sizeof(dp));
dp[1][0][0] = 0;
dp[1][1][1] = u[1];
Fdp();
ans = max(ans,dp[n][b][1]);
cout << ans << endl;
return ;
}
int main(){
cin >> T;
while(T --){
work();
}
return 0;
}