题意:
这个地方一天有 h 个单位时间,Vova打算睡觉睡 n 次觉
每次睡觉可以睡 a[ i ] 个单位时间或者 a[ i ] - 1 个单位时间,但必须要睡满
如果醒来的时间在 [ l , r ] 这个范围内,那么这个睡眠时间就很好(答案+1)
(醒来之后又要马上睡下一次的觉……)
问Vova该怎么安排这 n 次睡觉,才能使得好的睡眠时间次数最多
(即每次加上a[ i ]或a[ i ] - 1后的和对h取模,过程中会有多少次落在 l 与 r 之间)
解题思路:
动态规划,二维数组,一层记录第 i 次睡觉,另一层记录第 i 次睡醒的时间
dp[ i ] [ j ] 表示已经睡完 i 次觉,且第 i 次睡觉醒来的时间为 j 时,这个过程中答案的最大值
初始化数组为 -1 ,因为时间从 0 开始,所以特殊处理 dp[0][0] = 0
i = 1 ~ n j = 0 ~ h-1
i 表示第 i 次睡觉,j 表示第 i 次开始睡时的时间
所以只要 dp[ i-1 ][ j ] != -1,就可以进行状态转移(因为等于 -1 相当于没有任何一个过程到达过时间 j )
假设这一次要睡 t 个单位时间
则状态转移方程为
dp[i][t] = max( dp[i][t] , dp[i-1][j]+( t >= l && t <= r ? 1 : 0 ))
表示第 i 次醒来,醒来时间为 t 时的最大值,是由第 i 次睡觉,睡觉时间为 j 时的答案 与自身取大 转移而来
(46ms / 2000ms)
#include<bits/stdc++.h> using namespace std; int dp[2050][2050]; int main() { ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); memset(dp,-1,sizeof dp);//初始时,没有访问过的时间设置为-1 dp[0][0]=0;//如果某个时间有被访问过,那么dp就代表到达这个时间的这个过程中满足题意的最大个数 int n,h,l,r,i,j,a,t; cin>>n>>h>>l>>r; for(i=1;i<=n;i++) { cin>>a; for(j=0;j<h;j++)//每种时间都考虑一遍 { if(dp[i-1][j]!=-1) { t=(j+a)%h; dp[i][t]=max(dp[i][t],dp[i-1][j]+(t>=l&&t<=r?1:0));//如果下一个位置满足条件,使dp+1 t=(j+a-1+h)%h;//两种情况都在第i次进行考虑 dp[i][t]=max(dp[i][t],dp[i-1][j]+(t>=l&&t<=r?1:0)); } } } int ans=dp[n][0]; for(i=1;i<h;i++) ans=max(ans,dp[n][i]);//在最后一次结束后所在的时间中寻找最大值 cout<<ans<<' '; return 0; }