[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.html

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.git

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).github

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.ide

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

 

Note:post

  • 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.

 

Credits:
Special thanks to @stellari for adding this problem and creating all test cases.優化

 

這道王子救公主的題仍是蠻新穎的,我最開始的想法是比較右邊和下邊的數字的大小,去大的那個,可是這個算法對某些狀況不成立,好比下面的狀況:ui

1 (K) -3 3
0 -2 0
-3 -3 -3 (P)

若是按個人那種算法走的路徑爲 1 -> 0 -> -2 -> 0 -> -3, 這樣的話騎士的起始血量要爲5,而正確的路徑應爲 1 -> -3 -> 3 -> 0 -> -3, 這樣騎士的騎士血量只需爲3。無奈只好上網看大神的解法,發現統一都是用動態規劃 Dynamic Programming 來作,創建一個二維數組 dp,其中 dp[i][j] 用來表示當前位置 (i, j) 出發的起始血量,最早處理的是公主所在的房間的起始生命值,而後慢慢向第一個房間擴散,不斷的獲得各個位置的最優的生命值。逆向推正是本題的精髓所在啊,仔細想一想也是,若是從起始位置開始遍歷,咱們並不知道初始時應該初始化的血量,可是到達公主房間後,咱們知道血量至少不能小於1,若是公主房間還須要掉血的話,那麼掉血後剩1才能保證起始位置的血量最小。那麼下面來推導狀態轉移方程,首先考慮每一個位置的血量是由什麼決定的,騎士會掛主要是由於去了下一個房間時,掉血量大於自己的血值,而能去的房間只有右邊和下邊,因此當前位置的血量是由右邊和下邊房間的可生存血量決定的,進一步來講,應該是由較小的可生存血量決定的,由於較咱們須要起始血量儘量的少,由於咱們是逆着往回推,騎士逆向進入房間後 PK 後所剩的血量就是騎士正向進入房間時 pk 前的起始血量。因此用當前房間的右邊和下邊房間中騎士的較小血量減去當前房間的數字,若是是負數或着0,說明當前房間是正數,這樣騎士進入當前房間後的生命值是1就好了,由於不會減血。而若是差是正數的話,當前房間的血量多是正數也多是負數,可是騎士進入當前房間後的生命值就必定要是這個差值。因此咱們的狀態轉移方程是 dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])。爲了更好的處理邊界狀況,咱們的二維 dp 數組比原數組的行數列數均多1個,先都初始化爲整型數最大值 INT_MAX,因爲咱們知道到達公主房間後,騎士火拼完的血量至少爲1,那麼此時公主房間的右邊和下邊房間裏的數字咱們就都設置爲1,這樣到達公主房間的生存血量就是1減去公主房間的數字和1相比較,取較大值,就沒有問題了,代碼以下:this

 

解法一:

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m = dungeon.size(), n = dungeon[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        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) {
                dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]);
            }
        }
        return dp[0][0];
    }
};

 

咱們能夠對空間進行優化,使用一個一維的 dp 數組,而且不停的覆蓋原有的值,參見代碼以下:

 

解法二:

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m = dungeon.size(), n = dungeon[0].size();
        vector<int> dp(n + 1, INT_MAX);
        dp[n - 1] = 1;
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                dp[j] = max(1, min(dp[j], dp[j + 1]) - dungeon[i][j]);
            }
        }
        return dp[0];
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/174

 

相似題目:

Unique Paths

Minimum Path Sum

Cherry Pickup

 

參考資料:

https://leetcode.com/problems/dungeon-game/

https://leetcode.com/problems/dungeon-game/discuss/52774/C++-DP-solution

https://leetcode.com/problems/dungeon-game/discuss/52843/6-lines-16-ms-C++-O(mn)-Time-O(n)-Space

https://leetcode.com/problems/dungeon-game/discuss/52790/My-AC-Java-Version-Suggestions-are-welcome

 

LeetCode All in One 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索