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小时正在熟睡所能获得的最大体力!
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 }