【LeetCode-174】地下城游戏
问题
一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快到达公主,骑士决定每次只向右或向下移动一步。
编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
示例
-2 -3 3
-5 -10 1
10 30 -5
最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
解答1:动态规划
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m = dungeon.size(), n = dungeon[0].size(), dp[m][n];
// ------ 初始化
dp[m - 1][n - 1] = max(1, 1 - dungeon[m - 1][n - 1]);
for (int j = n - 2; j >= 0; j--)
dp[m - 1][j] = max(1, dp[m - 1][j + 1] - dungeon[m - 1][j]);
// ------
for (int i = m - 2; i >= 0; i--) {
dp[i][n - 1] = max(1, dp[i + 1][n - 1] - dungeon[i][n - 1]);
for (int j = n - 2; j >= 0; j--)
dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]);
}
return dp[0][0];
}
};
重点思路
这道题的dp是倒序的,这点很重要。
正序dp的含义为:从起点出发,到达位置[i,j]所需要的最少血量。倒序dp的含义为:从[i,j]出发,到达终点需要最少的血量。
由于本题有“加血”和“扣血”这样的状态,正序dp无法保证当前血量是否能到达终点,所以采用倒序dp。dp数组定义为:所需的最小血量到此格还剩的血量。状态转移方程为:dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
。
解答2:动态规划(状态压缩)
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m = dungeon.size(), n = dungeon[0].size(), dp[2][n];
// ------ 初始化
dp[(m - 1) & 1][n - 1] = max(1, 1 - dungeon[m - 1][n - 1]);
for (int j = n - 2; j >= 0; j--)
dp[(m - 1) & 1][j] = max(1, dp[(m - 1) & 1][j + 1] - dungeon[m - 1][j]);
// ------
for (int i = m - 2; i >= 0; i--) {
dp[i & 1][n - 1] = max(1, dp[(i + 1) & 1][n - 1] - dungeon[i][n - 1]);
for (int j = n - 2; j >= 0; j--)
dp[i & 1][j] = max(1, min(dp[(i + 1) & 1][j], dp[i & 1][j + 1]) - dungeon[i][j]);
}
return dp[0][0];
}
};