The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess.
The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately.
Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers).
In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step.
Write a function to determine the knight's minimum initial health so that he is able to rescue the princess.
For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN
.
-2 (K) | -3 | 3 |
-5 | -10 | 1 |
10 | 30 | -5 (P) |
Notes:
- The knight's health has no upper bound.
- Any room can contain threats or power-ups, even the first room the knight enters and the bottom-right room where the princess is imprisoned.
题意说的是,从一个矩阵的左上角走到右下角,每个格子里面代表的是血量需要加多少或者减多少。
然后需要血量在图中保持正数,在只能向右和向下的情况下,求从左上角到右下角需要的最小血量。
很明显用DP,但是如何用DP,刚开始的想法是,
1、用两个数组,一个数组记录到当前位置需要的最小血量min[len],另一个是在最小血量的情况下路径上的数的和sum[len]。
2、第一行因为只能一直向右走,所以很简单。
3、从第二行开始,有两种选项,从上面来还是从左边来(第一个单独算),然后比较这两种选项需要的最小血量,选择较小的那条路。
4、在两种选项的血量一样的时候,选择sum较大的走。
但是这样会出错。
[[1,-3,3],[0,-2,0],[-3,-3,-3]]这一组就会出错,因此这样是不对的。局部最优不等于全局最优。
public class Solution {//错误代码 public int calculateMinimumHP(int[][] dungeon) { if (dungeon.length == 1 && dungeon[0].length == 1){ if (dungeon[0][0] > 0){ return 1; } else { return -dungeon[0][0]+1; } } int len = dungeon[0].length; int[] min = new int[len]; int[] sum = new int[len]; int num = dungeon[0][0]; min[0] = Math.max(1, -num + 1); sum[0] = num; for (int i = 1; i < len; i++){ num += dungeon[0][i]; sum[i] = num; min[i] = Math.max(-num + 1, min[i - 1]); } int flag = 0; int flag2 = 1; for (int i = 1;i < dungeon.length; i++){ sum[0] += dungeon[i][0]; min[0] = Math.max(min[0], -sum[0] + 1); for (int j = 1;j < len; j++){ if (min[j - 1] < min[j] || (min[j - 1] == min[j] && sum[j - 1] > sum[j])){ sum[j] = sum[j - 1] + dungeon[i][j]; min[j] = Math.max(min[j - 1], -sum[j] + 1); } else { sum[j] += dungeon[i][j]; min[j] = Math.max(min[j], -sum[j] + 1); } } } return min[len - 1]; } }
2、反过来,从结尾开始,表示当前位置到结尾最少需要的血量
public class Solution { public int calculateMinimumHP(int[][] dungeon) { if (dungeon.length == 1 && dungeon[0].length == 1){ if (dungeon[0][0] > 0){ return 1; } else { return -dungeon[0][0] + 1; } } int len = dungeon[0].length; int row = dungeon.length; int[] min = new int[len]; min[len - 1] = -dungeon[row - 1][len - 1] + 1; min[len - 1] = Math.max(min[len - 1], 1); for (int i = len - 2; i >= 0; i--){ min[i] = min[i + 1] - dungeon[row - 1][i]; min[i] = Math.max(min[i], 1); } for (int i = row - 2; i >= 0; i--){ min[len - 1] = min[len - 1] - dungeon[i][len - 1]; min[len - 1] = Math.max(min[len - 1], 1); for (int j = len - 2; j >= 0; j--){ min[j] = Math.min(min[j + 1], min[j]) - dungeon[i][j]; min[j] = Math.max(min[j], 1); } } return min[0]; } }