【问题描述】[中等]
【解答思路】
1. 回溯(暴力)& 优化
超时,需要优化
public int calculateMinimumHP(int[][] dungeon) {
if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) {
return 0;
}
int rowLen = dungeon.length;
int colLen = dungeon[0].length;
// 最低的耗血量为 + 1;就是骑士的救公主的最低血量。
return dfs(0, 0, rowLen, colLen, dungeon) + 1;
}
public int dfs (int rowIndex, int colIndex, int rowSize, int colSize, int[][] dungeon) {
//
if (rowIndex >= rowSize || colIndex >= colSize) {
return Integer.MAX_VALUE;
}
// 退出条件
if (rowIndex == rowSize - 1 && colIndex == colSize - 1) {
if (dungeon[rowIndex][colIndex] >= 0) {
// 如果最后一个大于等于0,就返还0。
return 0;
} else {
//如果最后一个小于零,就返回负的值。
return -dungeon[rowIndex][colIndex];
}
}
// 右边格子的最优解,也就是最低的耗血量
int rightMin = dfs(rowIndex, colIndex + 1, rowSize, colSize, dungeon);
// 下边格子的最优解
int downMin = dfs(rowIndex + 1, colIndex, rowSize, colSize, dungeon);
// f(i,j) = min(f(i+1, j), f(i, j+1)) - dungeon[i][j]
int needMin = Math.min(rightMin, downMin) - dungeon[rowIndex][colIndex];
int res = 0;
if (needMin < 0) {
res = 0;
} else {
res = needMin;
}
System.out.println(rowIndex+ " "+colIndex + " " + res);
return res;
}
作者:fakerleet
链接:https://leetcode-cn.com/problems/dungeon-game/solution/cong-hui-su-dao-ji-yi-hua-sou-suo-dao-dong-tai-gui/
private int rowSize = 0;
private int colSize = 0;
private int[][] globalDun = null;
public int calculateMinimumHP(int[][] dungeon) {
if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) {
return 0;
}
rowSize = dungeon.length;
colSize = dungeon[0].length;
globalDun = dungeon;
int[][] memory = new int[rowSize][colSize];
// 初始化为-1,便于区别是否计算过结果了。
for (int i = 0; i < rowSize; ++i) {
for (int j = 0; j < colSize; ++j) {
memory[i][j] = -1;
}
}
return dfs(0, 0, memory) + 1;
}
public int dfs (int rowIndex, int colIndex, int[][] memory) {
if (rowIndex >= rowSize || colIndex >= colSize) {
return Integer.MAX_VALUE;
}
// 不为-1就是计算过了,直接返回结果。
if (memory[rowIndex][colIndex] != -1) {
return memory[rowIndex][colIndex];
}
if (rowIndex == rowSize - 1 && colIndex == colSize - 1) {
if (globalDun[rowIndex][colIndex] >= 0) {
return 0;
} else {
return -globalDun[rowIndex][colIndex];
}
}
int right = dfs(rowIndex, colIndex + 1, memory);
int down = dfs(rowIndex + 1, colIndex, memory);
int needMin = Math.min(right, down) - globalDun[rowIndex][colIndex];
int res = 0;
if (needMin < 0) {
res = 0;
} else {
res = needMin;
}
memory[rowIndex][colIndex] = res;
System.out.println(rowIndex+ " "+colIndex + " " + res);
return res;
}
作者:fakerleet
链接:https://leetcode-cn.com/problems/dungeon-game/solution/cong-hui-su-dao-ji-yi-hua-sou-suo-dao-dong-tai-gui/
2. 动态规划
思路一:紧接上文
问:深搜过程中的记忆化,真的能保证走过的路径是最优的吗?比如第一次搜索经过[2,2]这个点,把结果记录下来。下次再次搜索的时候,起点可能不一样了,这个时候有没有可能计算结果对[2,2]产生更新呢?
答:dfs的递归本身就是逆推的
第一次搜索[2,2] 看起来是第一次搜索 其实已经是右和下方的回滚了
public int calculateMinimumHPBest(int[][] dungeon) {
if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) {
return 0;
}
int rowSize = dungeon.length;
int colSize = dungeon[0].length;
int[][] dp = new int[rowSize][colSize];
// 设置最后一个值。
dp[rowSize - 1][colSize -1] = Math.max(0, -dungeon[rowSize - 1][colSize - 1]);
// 设置最后一列的值
for (int i = rowSize - 2; i >= 0; --i) {
int needMin = dp[i + 1][colSize - 1] - dungeon[i][colSize - 1];
dp[i][colSize -1] = Math.max(0, needMin);
}
// 设置最后一行的值
for (int i = colSize - 2; i >= 0; --i) {
int needMin = dp[rowSize - 1][i + 1] - dungeon[rowSize - 1][i];
dp[rowSize - 1][i] = Math.max(0, needMin);
}
for (int i = rowSize - 2; i >= 0; --i) {
for (int j = colSize - 2; j >= 0; --j) {
// 从右边和下边选择一个最小值,然后减去当前的 dungeon 值
int needMin = Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];
dp[i][j] = Math.max(0, needMin);
}
}
return dp[0][0] + 1;
}
作者:fakerleet
链接:https://leetcode-cn.com/problems/dungeon-game/solution/cong-hui-su-dao-ji-yi-hua-sou-suo-dao-dong-tai-gui/
思路二:
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int n = dungeon.length, m = dungeon[0].length;
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= n; ++i) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
dp[n][m - 1] = dp[n - 1][m] = 1;
for (int i = n - 1; i >= 0; --i) {
for (int j = m - 1; j >= 0; --j) {
int minn = Math.min(dp[i + 1][j], dp[i][j + 1]);
dp[i][j] = Math.max(minn - dungeon[i][j], 1);
}
}
return dp[0][0];
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/dungeon-game/solution/di-xia-cheng-you-xi-by-leetcode-solution/
【总结】
1. 动态规划流程
第 1 步:设计状态
第 2 步:状态转移方程
第 3 步:考虑初始化
第 4 步:考虑输出
第 5 步:考虑是否可以状态压缩
2.思路 DFS -> 记忆化DFS->动态规划
转载:https://leetcode-cn.com/problems/dungeon-game/solution/cong-hui-su-dao-ji-yi-hua-sou-suo-dao-dong-tai-gui/
参考:https://leetcode-cn.com/problems/dungeon-game/solution/di-xia-cheng-you-xi-by-leetcode-solution/