leetcode 地下城游戏

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快到达公主,骑士决定每次只向右或向下移动一步。

编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。

例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。

-2 (K) -3 3
-5 -10 1
10 30 -5 (P)

说明:

  • 骑士的健康点数没有上限。

  • 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

错误代码

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        //假设初始生命值为0,那么各条路途中,血量最小情况的最大值。
        int len = dungeon.length;
        int len1 = dungeon[0].length;
        int[][][] dp = new int[len][len1][2];
        dp[0][0][0] = dungeon[0][0];
        dp[0][0][1] = dungeon[0][0];
        for (int i = 1; i < len; i++) {
            dp[i][0][0] = dp[i-1][0][0] + dungeon[i][0];
            if (dp[i][0][0] < dp[i-1][0][1]) {
                dp[i][0][1] = dp[i][0][0];
            } else {
                dp[i][0][1] = dp[i-1][0][1];
            }
        }
        
        for (int i = 1; i < len1; i++) {
            dp[0][i][0] = dp[0][i-1][0] + dungeon[0][i];
            if (dp[0][i][0] < dp[0][i-1][1]) {
                dp[0][i][1] = dp[0][i][0];
            } else {
                dp[0][i][1] = dp[0][i-1][1];
            }
        }

        for (int i = 1; i < len; i++) {
            for (int j = 1; j < len1; j++) {
                int one = Math.min(dp[i][j-1][0] + dungeon[i][j], dp[i][j-1][1]);
                int two = Math.min(dp[i-1][j][0] + dungeon[i][j], dp[i-1][j][1]);
                if (one > two) {
                    dp[i][j][0] = dp[i][j-1][0] + dungeon[i][j];
                    dp[i][j][1] = one;
                } else if (one < two) {
                    dp[i][j][0] = dp[i-1][j][0] + dungeon[i][j];
                    dp[i][j][1] = two;
                } else {
                    if (dp[i][j-1][0] > dp[i-1][j][0]) {
                        dp[i][j][0] = dp[i][j-1][0] + dungeon[i][j];
                        dp[i][j][1] = one;
                    } else {
                        dp[i][j][0] = dp[i-1][j][0] + dungeon[i][j];
                        dp[i][j][1] = two;
                    }
                }
            }
        }
        return (dp[len-1][len1-1][1] * -1)<0?1:(dp[len-1][len1-1][1] * -1)+1;
    }
}

以上的代码的核心思路是,假设起始为0,从左上到右下搜索,各个途径中  哪个途径中出现的最小值, 相对较大,那么这个值*-1+1就是结果,但是在判断取左边还是上边的时候,我是按照当前路径中出现的最小值哪个更大来选择的(实际上不对,因为虽然现在已经出现的最小值 比较大,但是可能目前累加和比较小,加上之后的一个小的数字,导致最小值变小)如[[1,-3,3],[0,-2,0],[-3,-3,-3]]

正确代码

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        //假设初始生命值为0,那么各条路途中,血量最小情况的最大值。
        int len = dungeon.length;
        int len1 = dungeon[0].length;
        int[][] dp = new int[len+1][len1+1];
        for (int i = len; i >= 0; i--) {
            for (int j = len1; j >= 0; j--) {
                dp[i][j] = Integer.MAX_VALUE;
            }
        }
        dp[len][len1-1] = 1;
        for (int i = len-1; i >= 0; i--) {
            for (int j = len1-1; j >= 0; j--) {
                dp[i][j] = Math.max(1, Math.min(dp[i][j+1], dp[i+1][j])-dungeon[i][j]);
            }
        }
        return dp[0][0];
    }
}

当感觉从左上到右下搜索不可行的时候,可以尝试反过来,可以通过dp数组多一行多一列的方式 不需要处理边界条件,。

递推公式为 dp[i][j] = max(1,  min(dp[i+1][j], dp[i][j-1])-arr[i][j]);

猜你喜欢

转载自www.cnblogs.com/tobemaster/p/9393771.html