Naptime(dp中环的处理)

Naptime(luogu) 

双倍经验

Description

在某个星球上,一天由N小时构成。我们称0-1点为第一个小时,1-2点为第二个小时,以此类推。

在第i个小时睡觉能恢复Ui点体力。在这座星球上住着一头牛,它每天要休息B个小时,它休息的

这B个小时可以不连续,可以分成若干段,但是在每一段的第一个小时不能恢复体力,从第二个小

时开始才可以恢复体力。 为了身体健康,这头牛希望遵循生物钟,每天采用相同的睡觉计划。另

,因为时间是连续的,每天的第N个小时与下一天的第一个小时是相连的,这头牛只需要在N个小

时内休息B个小时就够了。 请你给这头牛安排一个任务计划,使得它每天恢复的体力最多。

N<=3830 0<Ui<200000

Sol

对于环的处理我们往往是复制一倍求区间解

但这道题复杂度上限为O(n2),不能这样,而且区间dp也不适合它,线性dp更适合

观察问题,发现可以分为两种情况:

  • 第n个小时和第1个小时连续休息
  • 第n个小时和第1个小时不连续休息

该时刻是否休息是一个关键,于是我们有了状态:

f[i][j][0/1]表示第1~i小时一共休息j小时并且第i小时不休息(0)/休息(1)所能获得最大体力

转移方程:f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1])

     f[i][j][1]=max(f[i-1][j-1][1]+U[i],f[i-1][j-1][0])    (j>0)

对于两种情况,我们分别令dp初值为

  • f[1][1][1]=U[1]
  • f[1][1][0]=0,f[1][0][0]=0

其他均为负无穷(因为不是前提)

目标为

  • max(f[n][b][1],f[n][b][0])
  • f[n][b][1]

 

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=3831;
int f[N][N][2],d[N],n,b,T;
void dp()
{
    for(int i=2;i<=n;i++)
        for(int j=0;j<=b;j++)
        {
            f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
            if(j) f[i][j][1]=max(f[i-1][j-1][1]+d[i],f[i-1][j-1][0]);
        }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&b);
        for(int i=1;i<=n;i++)
            scanf("%d",&d[i]);
        memset(f,0x80,sizeof(f));
        f[1][1][1]=f[1][0][0]=0;
        dp();
        int ans=max(f[n][b][0],f[n][b][1]);
        memset(f,0x80,sizeof(f));
        f[1][1][1]=d[1];
        dp();
        printf("%d\n",max(ans,f[n][b][1]));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hsez-cyx/p/12302264.html