Codeforces 1324E

题意:

这个地方一天有 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;
}

 

原文地址:https://www.cnblogs.com/stelayuri/p/12508208.html