代码展示:
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int m=dungeon.length;
int n=dungeon[0].length;
//创建dp数组
int[][]dp=new int[m+1][n+1];
//初始化
for(int i=0;i<=m;i++){
dp[i][n]=Integer.MAX_VALUE;
}
for(int j=0;j<=n;j++){
dp[m][j]=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.min(dp[i][j+1],dp[i+1][j])-dungeon[i][j];
dp[i][j]=Math.max(1,dp[i][j]);
}
}
//返回值
return dp[0][0];
}
}
通过动态规划来解决这道题,根据动态规划的5大步骤分析解答
1.状态表示
通常来说,我们可以从前到后进行状态表示,也可以从后向前进行状态表示,
从前到后进行状态表示的话我们可以设dp【i】【j】表示从起点到【i,j】位置时骑士的最小初始健康点
从后向前进行状态表示的话我们可以设dp【i】【j】表示从【i,j】位置到终点位置骑士最小的初始健康点
这里我们用第二种方式进行状态表示
2.状态转移方程
从最近的一步进行分析,我们要想从【i,j】位置到达终点位置,我们可以向右移动到【i,j+1】的位置,也可以向下移动到【i+1,j】的位置,而我们选择从哪里到终点呢,由于要求骑士的最低健康数,所以我们应该选择到达终点所需最低健康数低的那个位置去终点,而【i,j+1】,【i+1,j】的最低健康数即dp【i,j+1】,dp【i+1,j】,通过比较两个数的大小,选出小的那个位置
所以状态转移方程应该为:dp[i][j]=Math.min(dp[i][j+1],dp[i+1][j])-dungeon[i][j];
dungeon[i][j]表示当前位置是加血还是扣血,要是加血,那么我们的初始血量就可以减少,要是扣血,那么我们的初始血量就要增加,所以是相反关系
3.初始化
为了方便初始化,我们要通过添加辅助结点的方式辅助初始化,在创建dp数组时,相对于dungeon数组,我们要多添加一行一列,这是动态规划常用的方法。
要注意,填进辅助结点中的值要保证后续填表是正确的,通过分析,在对dp数组进行初始化时我们要将最后一行和最后一列除了 dp[m-1][n]和dp[m][n-1]设为1外,其余的都设为int类型的最大值
4.填充数组
由于dp【i,j】依靠dp【i,j+1】,dp【i+1,j】,所以行方向上采用从下往上,列方向上采用从右往左。
5.返回值
要知道从【0,0】位置到终点所需的最小健康值,所以返回dp【0】【0】