SP283 NAPTIME - Naptime 题解
蒟蒻第一次用这样的方式做环上dp,实在是想记录和总结一下。
环上dp的做法
条件
-
\(n\) 时间内总共睡 \(b\) 小时。
-
每一段睡觉区间第一段时间不参与贡献。
思路
这是一个环上dp,由于 \(n\) 有点小大,我们采取枚举断点状态的方式。
状态转移方程
\(dp_{i,j,1/0}\) 表示 \(i\) 时刻睡了 \(j\) 小时,\(1\) 表示睡了。
\[dp_{i,j,0}=\max(dp_{i-1,j,0},dp_{i-1,j,1})
\]
没睡则直接延续上一个时刻。
\[dp_{i,j,1}=\max(dp_{i-1,j-1,0},dp_{i-1,j-1,1}+a_i)
\]
睡了讨论上一个时刻的情况,上一个时刻没睡则本时刻为第一段时间,不参与贡献。
断点讨论
- \(n\) 时刻没睡,那么 \(i\) 时刻要么睡,要么为第一段时间。
dp[1][0][0]=0,dp[1][1][1]=0;
- \(n\) 时刻睡了,那么 \(i\) 时刻睡了可参与贡献,由于这是一个环,我们会在后面状态转移的时候到达 \(n\) 同样会有 \(n\) 时刻睡了的情况,所以不用担心 \(n\) 时刻的状态没有记录。
dp[1][0][0]=0,dp[1][1][1]=a[1];
code
//dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);
//dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+a[i]);
//i-1没睡,i睡了也没用
//i时刻,已经睡了j小时,当前睡没睡
#include<bits/stdc++.h>
//define int long long //不要开longlong,会mle。
using namespace std;
const int N=3840;
template<class T> T read(T &x){
char c=getchar();bool f=0;x=0;
while(!isdigit(c)) f|=c=='-',c=getchar();
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return f?-x:x;
}
int T,n,b,ans=-1;
int a[N];
int dp[N][N][2]; //第一篇题解状转更牛哦,新手学习这个没关系
int main(){
read(T);
while(T--){
read(n),read(b);
for(int i=1;i<=n;i++) read(a[i]);
memset(dp,-0x3f,sizeof(dp));//n时刻没睡
dp[1][0][0]=0;
dp[1][1][1]=0;
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]);//直接顺延
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+a[i]);
}
}
ans=dp[n][b][0];
memset(dp,-0x3f,sizeof(dp));////n时刻睡了
dp[1][0][0]=0;
dp[1][1][1]=a[1];
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]);
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+a[i]);
}
}
ans=max(ans,dp[n][b][1]);
printf("%d\n",ans);
}
}
/*
5 3
2
0
3
1
4
*/
/*
6
*/
蒟蒻拙见,请指教

浙公网安备 33010602011771号