思路来源:讨论区第一条评论
这题使用动态规划的方法来解。我们以示例为例来解释。
1.假设矩阵只有一个值,也就是右下角的-5,那么骑士需要6点血。
2.对于最下面一行来说,只能往右走,所以可以求出进入每一格需要的血量。在30这里,设需要的血量为x,进入下一格需要6点血,那么应该有x + 30 >=6, x >= -24, 算出来x小于0,说明这个格子是加血的,并且加的血足够,x = 1就可以了。那么最下面一行依次就是,{1,1,6}。同理最右边一列是{2,5,6}。
3.接下看递推公式,用hpMatrix表示骑士从任意一格到达终点所需要的最少血量,matrix表示输入矩阵。对于任意一格matrix[i][j]来说,骑士进入它并且能够成功到达终点所需要的血量,应该是进入这个格子本身消耗的血量,加上向右和向下其中需要较少的血量。也就是hpMatirix[i][j] = -matrix[i][j] + min(hpMatirx[i][j + 1], hpMatrix[i + 1][j])。 以-10这一格为例,进入它会消耗10滴血,它右边的格子需要5滴血,下边的格子需要1滴血,所以向下走,因此进入这一格骑士需要11滴血。
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int rows=dungeon.length;
int coloums=dungeon[0].length;
int[][] dp=new int[rows][coloums];
//初始化右下角的格子:
//如果在dungeon里右下角的数大于0,就是证明是加血的,那么进入到这个格子的最少血量只需要1;
//如果在dungeon里右下角的数小于0的话就说明这个格子是扣血的,用1减去这个负数,那么就能保证进入到这个格子至少还剩余1血
dp[rows-1][coloums-1]=dungeon[rows-1][coloums-1]>0? 1 : 1 - dungeon[rows-1][coloums-1];
//初始化最后一行,最后一行只能往右走
for(int j = coloums-2;j>=0;j--){
//要判断当前格子是加血的还是扣血的
if(dp[rows-1][j+1] - dungeon[rows-1][j]<=0)//1-30小于0,即说明当前格子是加血的
dp[rows-1][j] = 1;//所有到达这个格子时最小的生命值可以为1
else//如果大于0的话就说明当前格子是负数,进入当前格子至少比这个负数大1
dp[rows-1][j] = dp[rows - 1][j + 1] - dungeon[rows - 1][j];
}
//初始化最后一列,最后一列只能往下走
for(int i = rows-2;i>=0;i--){
if(dp[i+1][coloums-1] - dungeon[i][coloums-1] <=0)//说明是加血的
dp[i][coloums-1]=1;//进入当前格子生命最少是1就行
else
dp[i][coloums-1]=dp[i + 1][coloums-1] - dungeon[i][coloums-1];
}
// 从后往前处理剩余位置,取最小值
for (int i = rows - 2; i >= 0; --i) {
for (int j = coloums - 2; j >= 0; --j) {
//往右走需要多少血
int toLeft = dp[i][j + 1] - dungeon[i][j] > 0 ? dp[i][j + 1] - dungeon[i][j] : 1;
//往上走需要多少血
int toDown = dp[i + 1][j] - dungeon[i][j] > 0 ? dp[i + 1][j] - dungeon[i][j] : 1;
dp[i][j] = toLeft > toDown ? toDown : toLeft;
}
}
return dp[0][0];
}
}