传送门:洛谷P1156 垃圾陷阱
废话
只是单纯想把动态规划前前后后理清楚 其实是防止以后自己看不懂自己的代码,有些地方可能说的有点冗余降低了阅读体验,还请见谅
题目大意
奶牛初始存活时间为10。深度为H的井中隔一段时间会掉下来一个垃圾,一共n个。每个垃圾可以拿来加存活时间或者垫高度,每个垃圾掉下来前的等待会相应减少存活时间。一旦堆出的高度≥H就能跑出去,一旦存活时间<0则死亡。求出最快多久能出去;若活着出不去则求出最长存活时间。
思路
大致方向
一个垃圾只有堆或者吃两种选择
可以想象成一个背包有n个物品可供选择是吃还是堆,那么这道题就是一道类似01背包的动态规划 其实还是道淼题
状态定义
由于有时间这个状态而要求的恰恰就是最短的逃生时间,那么这道题的时间就对应了01背包的价值。考虑将dp值定义为时间
具体怎么定义?这还用问吗
首先以背包的定义方式,第一维是垃圾编号是肯定的 (数据范围这么小就不优化了)
还要一维记录背包容量,第二维就是高度
完整的状态就是dp[i][j]表示前i个垃圾堆的高度为j时奶牛剩余的最大存活时间
为什么时间是最大?求的不是最短时间吗? orz这是什么问题
使用同样前i个垃圾堆相同垃圾高度的情况下,剩余时间更大肯定是更优的
所以dp要不断更新最大值
转移方程
先根据题意初始化 dp[0][0] = 10 (没有垃圾掉下来,堆出的初始高度为0,能活10小时)
转移方程跟01背包别无大异无非就是多考虑一个垃圾之间的等待时间
显而易见吃的情况:
dp[i][j] = dp[i-1][j] + add_life - wait_time (要在线地减去等待时间,下同)
不过转移之前想想会出现什么别的情况?
没错。如果在当前第i个垃圾到来前奶牛时间就用光了,上一个状态是没法到达当前状态的
从方程得知当前状态 dp[i][j] 会从 dp[i-1][j] 转移过来
所以判断 if ( dp[i-1][j]-wait_time < 0 ), 若是则不能进行转移
堆的情况: (此时的 dp[i][j] 是上面吃的方程刚更新出来的或未更新的dp 数组初始值)
dp[i][j] = max(dp[i][j], dp[i-1][j - add_h] - wait_time)
同样要加一个上面的判断,if ( dp[i-1][j - add_h] - wait_time > 0)
并且还要加一个 if (j - add_h ≥ 0)防止访问负下标
由于 i j是按递增顺序正向枚举的,第一个满足 j ≥ H 并且 dp[i][j] ≥ 0 (还活着) 的状态就是答案,直接输出,return 0;
初始化还有一个全赋负数的操作,这是一个细节问题
因为 dp[i][j] ≥ 0 是答案产生的条件之一,且dp值为0的时候也是合法的,会计入最终答案
而定义数组时值默认是0
所以dp值要初始化成 < 0 防止未被更新的空状态变成答案!(这个漏了就是全WA)
结局2
dp枚举完了还没输出就是死在坑里了(悲)
从一开始一直吃垃圾吃到饿死,输出最终存活时间,return 0;
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int dp[101][101];
struct rubbish