174. 地下城游戏

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。 有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。 为了尽快到达公主,骑士决定每次只向右或向下移动一步。 编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。 例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7

-2 (K) -3 3
-5 -10 1
10 30 -5 (P)

说明:

  • 骑士的健康点数没有上限。
  • 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

解题思路 | 反向dp

参考【官方题解

反向动态规划,即反向 dp
1.状态定义
174. 地下城游戏 - 图1 表示从坐标 174. 地下城游戏 - 图2 到终点所需的最小初始值。
从终点 174. 地下城游戏 - 图3 开始倒推到起点计算每个坐标点的 dp 值
2.动态转移方程
174. 地下城游戏 - 图4
3.初始条件**:
1.令174. 地下城游戏 - 图5
2.令174. 地下城游戏 - 图6

举个栗子**lizi4.jpglizi1.jpglizi5.giflizi1.jpg**
如下图根据题目给的示例计算的dp值,圆圈里的数代表了此位置的dp值。
初始时除了174. 地下城游戏 - 图11174. 地下城游戏 - 图12等于 1 外,其他都为 174. 地下城游戏 - 图13
计算顺序是从位置174. 地下城游戏 - 图14开始计算dp值。如:174. 地下城游戏 - 图15
然后从174. 地下城游戏 - 图16计算到174. 地下城游戏 - 图17174. 地下城游戏 - 图18174. 地下城游戏 - 图19174. 地下城游戏 - 图20174. 地下城游戏 - 图21
image.png

代码实现

  1. /*
  2. 1.骑士的健康点数初始值必须 >= 1
  3. 1.骑士的健康点数任意时刻必须 >= 1
  4. */
  5. class Solution {
  6. public:
  7. int calculateMinimumHP(vector<vector<int>>& dungeon) {
  8. int n = dungeon.size();
  9. int m = n>0 ? dungeon[0].size():0;
  10. //初始条件
  11. vector<vector<int>> dp(n+1,vector<int>(m+1,INT_MAX));
  12. dp[n][m-1] = 1;dp[n-1][m] = 1;
  13. /*
  14. dp[i][j] 表示从坐标 (i,j)到终点所需的最小初始值。
  15. 反向dp,从终点开始计算,计算每个坐标点到终点所需的的最小初始值
  16. */
  17. for(int i=n-1;i >= 0;i--) {
  18. for( int j = m-1; j >=0; j--) {
  19. int tmin = min(dp[i+1][j],dp[i][j+1]);
  20. dp[i][j] = max(tmin - dungeon[i][j],1);
  21. }
  22. }
  23. return dp[0][0];
  24. }
  25. };