您的位置:首页 > 其它

算法学习之动态规划(leetcode 174. Dungeon Game)

2016-10-31 23:59 537 查看
leetcode 174. Dungeon Game

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.



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.

解答思路,其实就是求从左上角走到右下角的时候,使其体力值最小,要注意,直接求和有问题,因为中间如果有体力值小于0的情况,直接就结束了,不会继续。如序列(-8, 10),如果直接相加,则为2,体力为1就可以,但是实际上体力值为9才可以继续。

1 自己和王印讨论的思路(错误的思路)

想从左上角往右下角做,然后进行了一些推理性质的东西,后来发现,推理最开始的时候就不成立,下面将我俩的思路详述一下。

如下图



两个矩阵

第一个矩阵A[i][j],代表的意思是如果走完(i, j)后,还剩下多少体力值。

第二个矩阵B[i][j],代表的意思是如果走完(i, j)后,还剩下多少体力值。

思路

第一步:将第一行和第一列的A和B都进行初始化,此步骤是正确的,原因是,只有一条路到这里。

第二步:根据图中所示(i+1, j)和(i, j+1),推算出(i+1, j+1),此步骤有问题!!!

本来的思路是,根据试探性走(i, j),然后判断出来向下走还是向右走。但是此时只根据(i+1, j)和(i, j+1)和(i, j),无法判断出(i+1, j+1)位置的B[i+1][j+1],原因是因为不知道终点在哪儿,此时如果算出一个B[i][j],只能是一个局部的值,见我具体的代码和错误的示例。根据例子可以显然看出。

代码

public class Solution {
public int myCore(int n){
//n>0,不需要体力值
if(n > 0)   return 1;
//注意n = 0
else        return 1 - n;
}
public int calculateMinimumHP(int[][] dungeon) {
if(dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) return 0;

int[][] a = dungeon;

int rows = dungeon.length;
int cols = dungeon[0].length;

int[][] A = new int[rows][cols];
int[][] B = new int[rows][cols];

//初始化第一行 第一列
A[0][0] = a[0][0];
for(int i = 1; i < cols; i++){
A[0][i] = A[0][i-1] + a[0][i];
}
for(int i = 1; i < rows; i++){
A[i][0] = A[i-1][0] + a[i][0];
}
B[0][0] = myCore(a[0][0]);

for(int i = 1; i < cols; i++){
B[0][i] = Math.max(B[0][i-1], myCore(A[0][i]));
}
for(int i = 1; i < rows; i++){
B[i][0] = Math.max(B[i-1][0], myCore(A[i][0]));
}
//进行后续判断
int x = 0;
int y = 0;
for(int i = 1; i < rows; i++){
for(int j = 1; j < cols; j++){
x = Math.max(B[i-1][j], myCore(A[i-1][j]+a[i][j]));
y = Math.max(B[i][j-1], myCore(A[i][j-1]+a[i][j]));

if(x < y){
A[i][j] = A[i-1][j]+a[i][j];
B[i][j] = x;
}
else{
A[i][j] = A[i][j-1]+a[i][j];
B[i][j] = y;
}
}
}

return B[rows-1][cols-1];
}
}


结果为

36 / 44 test cases passed.
错误例子举例
输入为
[[1,-3,3],
[0,-2,0],
[-3,-3,-3]]
输出本来应该为3,但是我的结果却为5。
A矩阵如下
1 -2 1
1 -1 -1(here!)
-2 -4 -4
B矩阵如下
1 3 3
1 2 2(here!)
3 5 5
原因分析,看图中的here处,因为B[1][2]此处得到2是因为经过下->右->右,
即可得到,这没有问题,但是当需要做B[2][2]时候,其实最优的路径应该是
右->右->下->下,但是,由于前面计算失误,所以后续的肯定不是最少了。


这个思路错的最根本的原因,一是定义不清晰,应该定位为还没走B[i][j]时,当时的体力值。二是推理的时候不严谨,不能进行如此的推理,根基不明确

2 答案的思路

首先是定义,定义b[i][j]为走(i, j)之前的体力值。然后从右下角往前面推,这是因为最后肯定体力值为1,倒着推肯定是最少的。切着最底下的边走。



AC代码如下

public class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int[][] a = dungeon;
if(a == null || a.length == 0 || a[0].length == 0) return 0;

int rows = a.length;
int cols = a[0].length;

//定义一个与原数组相同大小的数组,
//b[i][j]的含义,到达此处时初始的体力值最小为多少
int[][] b = new int[rows][cols];
b[rows-1][cols-1] = Math.max(1, 1 - a[rows-1][cols-1]);

//初始化最后一行
for(int i = cols - 2; i >= 0; i--){
b[rows-1][i] = Math.max(b[rows-1][i+1] - a[rows-1][i], 1);
}
//初始化最后一列
for(int i = rows - 2; i >= 0; i--){
b[i][cols-1] = Math.max(b[i+1][cols-1] - a[i][cols-1], 1);
}

//从右下角向左上角更新
for(int i = rows - 2; i >= 0; i--){
for(int j = cols - 2; j >= 0; j--){
int left = Math.max(1, b[i+1][j] - a[i][j]);
int right = Math.max(1, b[i][j+1] - a[i][j]);

b[i][j] = Math.min(left, right);
}
}

return b[0][0];
}
}


动态规划这儿还需要多下功夫。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息