The demons had captured the princess § and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess.
The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately.
Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0’s) or contain magic orbs that increase the knight’s health (positive integers).
In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step.
Write a function to determine the knight’s minimum initial health so that he is able to rescue the princess.
For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN.
Note:
The knight’s health has no upper bound.
Any room can contain threats or power-ups, even the first room the knight enters and the bottom-right room where the princess is imprisoned.

骑士从左上角的(K)出发,(Knight: 骑士),走到正数的格子是加那么多数值的血量,负数的格子是掉血,问从左上角出发走到右下角解救公主(P:Princess)的位置,刚开始左上角的位置至少需要多少血量。
注意在任意一格如果血量降为0,就挂掉了,不能再继续往前走,所以每一格的血量最少是1
思路:
首先看最后P的位置,到达这个位置减掉这个格子的血量之后,至少要剩余1的血量
x - 5 = 1 -> x = 6, 所以要带着6的血量进入P的格子,才不会挂掉
倒推:现在从P上面一格出发,进入这一格会+1, 要保证在这一格保留6的血量才能进入P,所以x+1 = 6 -> x = 5
所以定义一个dp数组,假设dp[i][j]表示从[i][j]出发需要的血量,那么
向右走时: dp[i][j] + dungeon[i][j] = dp[i][j+1]
向下走时: dp[i][j] + dungeon[i][j] = dp[i+1][j]
而只需要在这两种路径中取较小的
所以:
dp[i][j] = min(dp[i][j+1], dp[i+1][j]) -dungeon[i][j]
另外还要保证dp[i][j]至少是1
边界情况只需要考虑一个方向就行了
所以可以得到以下数组:
| 7 | 5 | 2 |
|---|---|---|
| 6 | 11 | 5 |
| 1 | 1 | 6 |
最后返回dp[0][0]就行了,就是从[0][0]出发时的血量
public int calculateMinimumHP(int[][] dungeon) {
if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) {
return 0;
}
int height = dungeon.length;
int width = dungeon[0].length;
int[][] dp = new int[height][width];
//P position..
dp[height - 1][width - 1] = Math.max(1, 1 - dungeon[height - 1][width - 1]);
//right border..
for (int i = height - 2; i >= 0; i--) {
dp[i][width - 1] = Math.max(1, dp[i + 1][width - 1] - dungeon[i][width - 1]);
}
//down border..
for (int j = width - 2; j >= 0; j--) {
dp[height - 1][j] = Math.max(1, dp[height - 1][j + 1] - dungeon[height - 1][j]);
}
//other grids..
for (int i = height - 2; i >= 0; i--) {
for (int j = width - 2; j >= 0; j--) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];
dp[i][j] = Math.max(1, dp[i][j]);
}
}
return dp[0][0];
}
博客围绕骑士从左上角出发,在二维网格状的地牢中解救右下角公主的问题展开。骑士在不同房间会增减血量,血量为 0 则死亡。通过动态规划思路,定义 dp 数组,倒推计算从各位置出发所需的最小血量,最终得出从左上角出发的最小初始血量。
937

被折叠的 条评论
为什么被折叠?



