描述:
he 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.
简单表述就是有个MxN的格子,每个格子里有一个数,或正或负(也可为0),要从左上角走到右下角,只能向右或者向下走,每走一个格子便要加上格子里的数,如果中途变成小于等于0就失败,问给初始最小多少可以走到右下角.
最初想到的从左上角开始向右或者向下找最大的,直到到达右下角,但这种方法显然错误,比如:
-1 | -2 | -100 | -1000 |
-3 | -20 | 1500 | -1000 |
4 | 3 | 2 | 1 |
正确的路径应该是-1>>-3>>4>>3>>2>>1,但是这样会选择-1>>-2>>-20>>1500>>2>>1这条路径,原因是没有考虑到过程中的最小值,这样只会到达右下角使得值最大.
要想使得满足题意可以用二分法,让你在左上角开始时携带一个数,比如10,你拿着这个数看能不能走到右下角,在路径中不能出现小于0,这就变成了寻找一条可以到达的路径,选择10如果到达后大于0,说明10这个"携带数"可以变得更小,二分成5,携带者5再去找路径,找不到就二分成(5+10)/2,以此类推,最终肯定能找到问题所要求的那个最小值.这种方法运行的次数还依赖于你所选得初始"携带数",显然这不是最优解.
我们可以考虑从右下角开始向左上角找路径,dp_table[i][j]代表在第i行j列这个格子时要到达右下角所要携带最小数字,例如题意给的3x3表,先观察下面几个数
-10 | 1 |
30 | -5 |
在-5处初始至少为5,要想从1出到达需要5-1=4,从30处到达需要5-30=-25,表示你什么都不需要携带就可以到达终点,那就是0,依次,在-10除呢,-10右边是4,下边是0,
4-(-10)=14,意味向右需要带着14才行,而向下需要0-(-10)=10,显然我们带着小的呀,那么我们动态规划的表就是:
10 | 4 |
0 | 5 |
可以列出我们所需要的式子:dp_table[i][j]=Math.max(Math.min(dp_table[i][j+1],dp_table[i+1][j])-dungeon[i][j],0); dungeon[i][j]表示初始表里的数字.
结果现在很明朗了,就是dp_table[0][0]+1;下面是Java代码实现.
1 public int calculateMinimumHP(int[][] dungeon) { 2 int m=dungeon.length-1; 3 int n=dungeon[0].length-1; 4 int[][] dp_table=new int[m+1][n+1]; 5 6 dp_table[m][n]=Math.max(0-dungeon[m][n],0); //初始最后一个元素 7 for(int i=m-1;i>=0;i--){ //初始最右边一列 8 dp_table[i][n]=Math.max(dp_table[i+1][n]-dungeon[i][n],0); 9 } 10 for(int j=n-1;j>=0;j--){ //初始化最下边一排 11 dp_table[m][j]=Math.max(dp_table[m][j+1]-dungeon[m][j],0); 12 } 13 for(int i=m-1;i>=0;i--) //初始化动态规划表中剩余的 14 for(int j=n-1;j>=0;j--){ 15 dp_table[i][j]=Math.max(Math.min(dp_table[i][j+1],dp_table[i+1][j])-dungeon[i][j],0); 16 } 17 return dp_table[0][0]+1; 18 }