地下城游戏
题目链接: 174. 地下城游戏
状态表示:
按照以往题的表示,dp[i][j]表示:从起点(0,0)位置到达(i,j)位置时,所需的最小初始健康值。但是如果这么去表示,不仅要考虑到达(i,j)位置的最小初始健康值,由于魔法球的存在,还需要考虑到达(i,j)位置时的健康值,因为魔法球会对算后续位置的最小初始健康值产生影响
下面用题目中的示例1为例,演示:
由此可知,到达魔法球位置所需的最低初始健康值和上一次的最低初始健康值保持一致,而魔法球会增加健康值,这就会对后面的结果产生影响,因此我们不仅要考虑到达(i,j)位置的最小初始健康值,还需要考虑到达(i,j)位置时的健康值,以保证后续结果的正确性
因此,我们可以试着用dp[i][j]表示:以(i,j)位置为起点,到达终点位置时,所需的最小健康值 。当(i,j)位置是魔法球时,可以用之前的dp[i+1][j]和dp[i][j+1]中的最小健康值减去治疗量,就能得到当前的位置到达终点位置时所需的最小健康值dp[i][j](注意:dp[i][j]不能小于0,最小值为1);当(i,j)位置是恶魔时,也是这样处理,实际上就是加上了需要扣除的健康值
通过这种状态表示,我们最终能够求得结果!
总结:
- 这道题的难点在于怎么去处理健康值增加的问题,健康值的增加不能为之前的损失提供帮助,只会对后续有帮助
- 如果按照第一种状态表示,dp[i][j]仅仅只表示了从(0,0)位置到达(i,j)位置所需的最小初始健康值,而由于魔法球的存在,导致后续的健康值会增加,因此我们还需要去记录当前位置的健康值,以保证后续计算最小初始健康值的正确性
- 如果按照第二种状态表示,dp[i][j]表示从(0,0)位置出发,按照最优路径到达(i,j)位置时,还需要剩余的最小健康值(为了到达终点后,健康值为1)。即dp[i][j]不仅表示了从(i,j)位置到达终点位置所需的最小初始健康值,还表示了从(0,0)位置出发到达(i,j)位置时,所需剩余的最小健康值(即当前健康值)
- 比较两种状态表示,可知,第二种表示更合理,更方便后续的填表
状态转移方程
dp[i][j] = min(dp[i+1][j],dp[i][j+1])-d[i][j],dp[i][j] = max(1, dp[i][j])
初始化
创建表时,多创建一行(第m行)和一列(第n列),除dp[m][n-1] = 1(dp[m-1][n] 也可初始化为1,表示救出公主后还需剩余1点健康值),其他都初始化为正无穷(以防对填表产生影响)
填表顺序
从下往上,每一行从右往左
返回值
dp[0][0]
实现代码
java
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
//1.创建dp表
int m = dungeon.length;
int n = dungeon[0].length;
int[][] dp = new int[m+1][n+1];
//2.初始化
for(int row = 0; row < m+1; row++) {
dp[row][n] = Integer.MAX_VALUE;
}
for(int col = 0; col < n+1; col++) {
dp[m][col] = Integer.MAX_VALUE;
}
dp[m][n-1] = 1;
//3.填表
for(int i = m-1; i >= 0; i--) {
for(int j = n-1; j >= 0; j--) {
dp[i][j] = Math.min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j];
dp[i][j] = Math.max(1, dp[i][j]);
}
}
//4.返回值
return dp[0][0];
}
}