POJ2228 Naptime 【例题精讲】

本文介绍了一种解决环状结构动态规划问题的方法,通过两次动态规划分别处理正常情况和特殊边界条件,确保覆盖所有可能的睡眠时间段组合,从而求得最大收益。

   题意:将一天分为N小时,每小时都有一个价值w,有一头牛要睡觉,而它的睡觉是连续的,且第一小时不能算价值,即如果你睡了[a,b],则你获得的收益是w[a+1]+w[a+2]+……+w[b],而这头牛可以每天多次睡(可以理解成选若干个时间段睡觉),不过每天的睡觉总时间数不能超过B,求能获得的最大总收益。(不过值得注意的是,“1天”并不是从0~N-1,而可以是从任何一个小时开始到n小时之后,即可以从N-1睡到0)

 
      一天有N个小时,然后月落日升,昼夜往复,很显然这是一道环状结构上的DP问题,我们一般遇到环状问题时,都是把环状结构拆成一条链,来覆盖拆环方式造成的结果不同。但是这道题,把n变成二倍然后枚举,恐怕会造成时间复杂度过大。所以我们会采用另一种办法!
我们可以简单的想到转移方程
f[i][j][0]代表某天前i小时睡了j小时,且第i小时醒着所能获得的最大体力;
f[i][j][1]表示前i小时睡了j小时,且第i小时正在熟睡所能获得的最大体力!
之后呢,因为我们丢了一种情况,就是这一天的第一个小时我们是可能会处于熟睡状态的,那怎么办呢!
我们可以强行令第一小时就在熟睡状态,并且上一天的第n小时就已经开始睡眠了,这样我们只需要跟上面的结果取最优就好了!赋初值的时候呢,当然就是要把第一小时f[1][1][1]的初值赋成每天第一小时睡觉获得的最大体力咯!
 
下面上代码!
 
 
 1 //看海天一色 听风起雨落
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<string>
 6 #include<cmath>
 7 #include<cstring>
 8 #include<algorithm>
 9 using namespace std; 
10 const int MAXN=3835;
11 int f[2][MAXN][2],w[MAXN],ans=0;//f[i][j][0]代表某天前i小时睡了j小时,且第i小时醒着;
12 // f[i][j][1]表示前i小时睡了j小时,且第i小时正在熟睡!
13 //加了一个滚动数组的优化! 
14 int n,m,k;
15 int main()
16 {
17     scanf("%d%d",&n,&m);
18     for(int i=1;i<=n;i++){
19         scanf("%d",&w[i]);
20     }
21     if(m==0){
22         puts("0");return 0;
23     }
24     memset(f,-0x3f,sizeof(f));
25     f[1&1][1][1]=0;
26     f[1&1][0][0]=0;//初始化! 
27     for(int i=2;i<=n;i++){
28         for(int j=0;j<=m;j++){
29             f[i&1][j][0]=max(f[(i-1)&1][j][1],f[(i-1)&1][j][0]);//对于没熟睡的转移 
30             if(j>=1) f[i&1][j][1]=max(f[(i-1)&1][j-1][0],f[(i-1)&1][j-1][1]+w[i]);//熟睡了的转移 
31         }
32     }
33     ans=max(f[n&1][m][1],f[n&1][m][0]);
34     memset(f,-0x3f,sizeof(f));
35     f[1&1][1][1]=w[1];//强行让牛在第一个小时就熟睡!这是前文规划中没有的部分;
36     for(int i=2;i<=n;i++){//执行第2次dp,对上面已经执行过的dp作补充! 
37         for(int j=0;j<=m;j++){
38             f[i&1][j][0]=max(f[(i-1)&1][j][1],f[(i-1)&1][j][0]);
39             if(j>=1) f[i&1][j][1]=max(f[(i-1)&1][j-1][0],f[(i-1)&1][j-1][1]+w[i]);
40         }
41     } 
42     ans=max(ans,f[n&1][m][1]);//两次取最优! 
43     printf("%d",ans);
44     puts("");
45     return 0;
46 }
View Code

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/8723341.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值