题目链接: leetcode.cn/problems/du…
题目描述:
恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为_负整数_,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为_正整数_,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。
注意: 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
示例 1:
输入: dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]] 输出: 7 解释: 如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7 。
示例 2:
输入: dungeon = [[0]]
输出: 1
提示:
m == dungeon.lengthn == dungeon[i].length1 <= m, n <= 200-1000 <= dungeon[i][j] <= 1000
题目解析
这题因为每个房间的最低健康点数是需要依赖于邻房间的最低健康点数的,例如我们所以我们要计算每一个房间的最低健康点数,使用动态规划来解决这些子问题更容易。
那么我们定义 dp[i][j] 的含义:
如果我们定义 dp[i][j] 定义为是从起点出发,到达 i、j 位置的时候,所需要的最低健康点数。是解决不了这题的,因为你到达 i、j 这个点后,你不仅需要知道本身的消耗健康值,你还需要知道后续路线的健康值,这就相当于什么,你站在起始点,你除了需要知道本身位置消耗的健康值,还需要知道从原地到终点的所有消耗健康值,这是不现实的。
所以我们将 dp[i][j] 定义为 i、j 为起点,到达终点所需要的最低健康值,为什么要这样定义?首先我们可以知道到达终点的最低健康值是 1,因为你到达终点之后日过是 0 的话你就死亡了。知道终点的话我们就可以往前一步一步推(倒推),假设 i、j 为终点前一步,也就是倒数第二步,那么我们知道了最后一步需要健康值,也知道了本身位置需要的健康值,就可以推出 dp[i][j]。
解决了定义之后就是解决状态转移方程,题目中使用 dungeon[i][-j] 来表示每个房间的健康点数,如果我们下一步是往右走的话,这个位置的 dp 值应该是等于右边位置的 dp 值减去我们此位置的 dungeon 值,也就是 dp[i][j] = dp[i][j+1] - dungeon[i][j];
同理往下走的话就是 dp[i][j] = dp[i+1][j] - dungeon[i][j],
但是我们要注意的是 dungeon[i][j] 可能为正数,如果算出来的 dp[i][j] 为负的话代表的意思就是,到这个房间不需要健康值,这是不现实的,你都为负数了就 g 了,所以如果为负数的话我们就给他赋值为 1。
下面就是代码细节,我们创建一个长和宽比原来高一个单位的二维数组来代表 dp 表,长宽加一就是为了不需要加边界的判断条件。然后初始化的时候需要将最后一排和倒数最后一列初始化为最大的数,这步是为了不影响数据。还有将 dp[m][n - 1] = dp[m - 1][n] = 1; 因为我们最终到达终点需要最小的步数就是 1.
代码
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int m = dungeon.length;
int n = dungeon[0].length;
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= n; i++) dp[m][i] = Integer.MAX_VALUE;
for (int i = 0; i <= m; i++) dp[i][n] = Integer.MAX_VALUE;
dp[m][n - 1] = dp[m - 1][n] = 1;
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
dp[i][j] = Math.min(dp[i][j + 1], dp[i + 1][j]) - dungeon[i][j];
dp[i][j] = Math.max(1, dp[i][j]);
}
}
return dp[0][0];
}
}