题目:174.地下城游戏
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
// 正向思维(自顶向下):走到任何一个位置时,血量都不能少于1并且到达终点时为1。
// 最低初始健康点数(最小为1)a + 路径中血(增/减)逐个相加出现的最低值(负值)b >= 1
// 但是从左上往右下的顺序注定a和b都会影响后续的决策。也就是说,这样的动态规划是不满足「无后效性」。
// 逆向思维(自底向上):
// 在每个路径上的点满足要求,最终求起点的血量,从终点(只剩1血)开始逐渐向左上角移动。由:
// x-2 >= 1
// x-2-3 >= 1
// x-2-3+3 >= 1
// x-2-3+3+1 >= 1
// x-2-3+3+1-5 = 1
// 反过来:
// x-2-3+3+1 = 1+5
// x-2-3+3 = 1+5-1
// x-2-3 = 1+5-1-3
// x-2 = 1+5-1-3+3
// x = 1+5-1-3+3+2 即 x = 7, 这样就无需考虑b且无后效性问题
// dp[i][j] = max(min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j], 1)
int m = dungeon.length;
int n = dungeon[0].length;
int[][] dp = new int[m+1][n+1];
Arrays.fill(dp[m], Integer.MAX_VALUE); // 数组批量赋值,只能一位数组
for(int j=0; j<=m; j++)
dp[j][n] = Integer.MAX_VALUE;
dp[m-1][n] = 1;
dp[m][n-1] = 1;
for(int i=m-1; i>=0; i--)
for(int j=n-1; j>=0; j--)
dp[i][j] = Math.max(Math.min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j], 1);
return dp[0][0];
}
}
看到一位网友的C++代码也很简洁:
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int n=dungeon.size(),m=dungeon[0].size();
vector<vector<int>> dp(n+1,vector<int>(m+1,INT_MAX));
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--){
dp[i][j]=max((min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]),1);
}
}
return dp[0][0];
}
};