Leetcode 174. Dungeon Game

問題描述

table.dungeon, .dungeon th, .dungeon td { border:3px solid black; } .dungeon th, .dungeon td { text-align: center; height: 70px; width: 70px; }

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)

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.

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

問題分析:

乍一看,這個問題和"Maximum/Minimum Path Sum"問題很相似。然而,具有全局最大HP(生命值)收益的路徑並不一定可以保證最小的初始HP,因爲題目中具有限制條件:HP不能≤0。例如,考慮下面的兩條路徑:0 -> -300 -> 310 -> 0 和 0 -> -1 -> 2 -> 0。這兩條路徑的淨HP收益分別是-300 + 310 = 10 與 -1 + 2 = 1。第一條路徑的淨收益更高,但是它所需的初始HP至少是301,才能抵消第二個房間的-300HP損失,而第二條路徑只需要初始HP爲2就可以了。

幸運的是,這個問題可以通過“table-filling”(表格填充)動態規劃算法解決,與其他"grid walking"(格子行走)問題類似:

首先,我們應該維護一個2維數組D,與地牢數組的大小相同,其中D[i][j]代表可以保證騎士在進入房間(i,j)之前,探索其餘地牢時能夠存活下來的最小HP。顯然D[0][0]就是我們隨後需要的最終答案。因此,對於這個問題,我們需要從右下角到左上角填充表格。

然後,我們來計算離開房間(i,j)時的最小HP。從這一點出發只有兩條路徑可選:(i + 1, j)和(i, j + 1)。當然我們會選擇擁有更小D值的那個房間,換言之,騎士完成剩下的旅途所需的較小HP。因此我們有:

  min_HP_on_exit = min(D[i+1][j], D[i][j+1])

現在D[i][j]可以通過dungeon[i][j]和min_HP_on_exit,根據下面的情景得出:

如果dungeon[i][j] == 0,那麼在這個房間裏很安全。 騎士離開這個房間時的HP和他進入房間時的HP保持一致, 也就是說 D[i][j] = min_HP_on_exit.

如果dungeon[i][j] < 0,那麼騎士在進入該房間之前的HP > 離開房間時的HP,min_HP_on_exit才能抵消他在該房間中的HP損失。 最小HP花費就是 "-dungeon[i][j]", 因此我們有公式 D[i][j] = min_HP_on_exit - dungeon[i][j].

如果dungeon[i][j] > 0, 那麼騎士在進入房間(i, j) 時的HP只需爲min_HP_on_exit - dungeon[i][j],因爲他可以在該房間內獲得數值爲"dungeon[i][j]"的HP收益。 不過,這種情況下min_HP_on_exit - dungeon[i][j]的數值可能≤0。 此時,我們需要把值置爲1以確保D[i][j]爲正整數: D[i][j] = max(min_HP_on_exit - dungeon[i][j], 1)。

注意 dungeon[i][j] > 0 條件下的等式實際上可以覆蓋其他兩種情況。 因此我們可以把三種情況歸納爲同一個等式: 亦即:

D[i][j] = max(min_HP_on_exit - dungeon[i][j], 1)
dungeon[i][j]可以爲任意值。

D[0][0]就是最終答案。 此外,像許多其他"table-filling"問題一樣,二維數組D可以用一維滾動數組替代。

代碼如下:

    public int calculateMinimumHP(int[][] dungeon) { 
        int m = dungeon.length;
        int n = dungeon[0].length;

        int[][] health = new int[m][n];

        health[m - 1][n - 1] = Math.max(1 - dungeon[m - 1][n - 1], 1);

        for (int i = m - 2; i >= 0; i--) {            
            health[i][n - 1] = Math.max(health[i + 1][n - 1] - dungeon[i][n - 1], 1);
        }

        for (int j = n - 2; j >= 0; j--) {
            health[m - 1][j] = Math.max(health[m - 1][j + 1] - dungeon[m - 1][j], 1);
        }

        for (int i = m - 2; i >= 0; i--) {
            for (int j = n - 2; j >= 0; j--) {
                int down = Math.max(health[i + 1][j] - dungeon[i][j], 1);
                int right = Math.max(health[i][j + 1] - dungeon[i][j], 1);
                health[i][j] = Math.min(right, down);
            }
        }
        return health[0][0];
    }

轉載原始鏈接:
http://bookshadow.com/weblog/2015/01/07/leetcode-dungeon-game/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章