Woodyiiiiiii / LeetCode

My private record of Leetcode solution

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

LeetCode 174. Dungeon Game

Woodyiiiiiii opened this issue · comments

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)

Note:

  • 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的思路,创建一个DP二维数组,从左上角元素开始往右/下前进,然而出现的问题是我们一开始并不知道骑士的血量是多少,其并不是依赖于第一个元素。而且到了公主房间时陷阱的正负还会有影响(所以我只能一部分AC)。

大佬们的思路是倒着来,从右下角元素开始往左上DP。一开始的数组容量设置为m + 1行,n + 1列,因为防止数组越界的情况;然后调用Arrays.fill函数将每行数组元素填充为整型最大值。每个dp[i][j](0 < i < m, 0 < j < n)代表进入当前房间的理想最小值,状态转移函数是用右/下的元素取最小值再减去房间内的陷阱值(正向是+,负向所以是-),因为要求进入房间的骑士血量要正值,所以取结果和1的最大值,用来刷新骑士血量。这样,一开始的dp[m - 1][n - 1]元素从初始化为1的边界元素dp[m][n - 1] 或dp[m-1][n]开始取最小值再减去陷阱,以此实现动态规划。

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int m = dungeon.length,n = dungeon[0].length;
        int[][]dp = new int[m + 1][n + 1];
        for(int i = 0 ; i <= m;i++){
            Arrays.fill(dp[i],Integer.MAX_VALUE);
        }
        dp[m][n - 1] = 1;
        dp[m-1][n] = 1;
        
        for(int i = m-1; i>=0;i--){
            for(int j = n-1 ; j>=0 ; j--){
                int minHp = Math.min(dp[i + 1][j],dp[i][j + 1]) - dungeon[i][j];
                dp[i][j] = minHp < 1 ? 1 : minHp;   
            }
        }
   
        return dp[0][0];
    }
}

将空间复杂度可以缩小至O(n),此时不能设置dp[n] = 1,因为除了最后一行,其他行也会使用dp[n]:

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int m = dungeon.length,n = dungeon[0].length;
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[n - 1] = 1;
        
        for(int i = m-1; i >= 0; --i){
            for(int j = n - 1 ; j >= 0 ; --j){
                int minHp = Math.min(dp[j],dp[j + 1]) - dungeon[i][j];
                dp[j] = minHp < 1 ? 1 : minHp;   
            }
        }
   
        return dp[0];  
    }
}

参考资料: