题目:174. 地下城游戏
思路:动态规划。
假设有一条路径:-2,-5,10,30,-5。
如果正向找初始值,我们需要存两个值,满足需求的起点健康值和达到每个位置时需要的最少健康值:
每个位置需要的最少健康值依赖前一个位置;满足需求的起点健康值就是在每个位置需要的最少健康值中取最大的(1也参与比较,因为有可能路径全为正值)。
如果反向找:下图从右到左看。我们无需额外计算起点健康值,在起点处的值就是我们想要的答案。
所以,我们动态规划是从终点开始搜索。
dp[i][j]
表示坐标(i,j)
到达终点需要的健康点。
由于只能向右或向下走,我们填充dp[i][j]
数组就依赖dp[i + 1][j]
和dp[i][j + 1]
,二者取较小的;然后再加上坐标(i,j)
消耗的点数;最后和1比较取较大的(路径正值)。
综上,转移方程:
dp[i][j] = Math.max(Math.min(dp[i + 1][j], dp[i][j + 1])- dungeon[i][j], 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 < m + 1 ;i ++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
dp[m][n - 1] = 1;
dp[m - 1][n] = 1;
for (int i = m - 1; i >= 0 ; i --) {
for (int j = n - 1; j >= 0; j --) {
int temp = Math.min(dp[i + 1][j], dp[i][j + 1]);
dp[i][j] = Math.max(temp - dungeon[i][j], 1);
}
}
return dp[0][0];
}
}
回溯超时代码:
class Solution {
private int res = Integer.MAX_VALUE;
public int calculateMinimumHP(int[][] dungeon) {
List<Integer> path = new ArrayList<>();
backtrack(dungeon, dungeon.length, dungeon[0].length, 0, 0, path);
return res;
}
public void backtrack(int[][] dungeon, int m, int n, int i, int j, List<Integer> path) {
if (i == m - 1 && j == n - 1) {
path.add(dungeon[i][j]);
res = Math.min(res, getMin(path));
path.remove(path.size() - 1);
return;
}
path.add(dungeon[i][j]);
if (i + 1 < m) {
backtrack(dungeon, m, n, i + 1, j, path);
}
if (j + 1 < n) {
backtrack(dungeon, m, n, i, j + 1, path);
}
path.remove(path.size() - 1);
}
public int getMin(List<Integer> path) {
int min = 1;
int left = 1;
for (int num : path) {
left -= num;
min = Math.max(min, left);
}
return min;
}
}