174. Dungeon Game

因為要求的是 在 0,0 的初始生命最少要多少
所以 如果dp[i][j] 狀態是由 dp[i+1][j], dp[i][j+1] 決定的話, 變成要從 m-1, n-1 推過來

跟一般 robot 走到右下角, 是相反的方式, 因為那種是 dp[i][j] 狀態是由 dp[i-1][j], dp[i][j-1]
一路推到 m-1, n-1, 才會知道總共的走法


T: O(mn)
S: O(mn)
```java
class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int m = dungeon.length;
        int n = dungeon[0].length;
        int[][] dp = new int[m][n];

        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                if (i == m-1 && j == n-1) {
                    dp[i][j] = 1 - dungeon[i][j]; // so in m-1, n-1, needs health 6 (dp[2][2]) to live (6 - 5 == 1)
                } else if (i == m-1) {
                    dp[i][j] = dp[i][j+1] - dungeon[i][j]; // dp[2][1] = dp[2][2] - dungeon[2][1] = 6 - 30 = -24 -> but at least 1! so dp[2][1] = 1
                } else if (j == n-1) {                     // so dp[2][0] also = 1
                    dp[i][j] = dp[i+1][j] - dungeon[i][j];
                } else {
                    dp[i][j] = Math.min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j];
                }
                dp[i][j] = Math.max(dp[i][j], 1); // 如果是補藥, dp 會變負的, 代表後來會吃到補藥, 變成負的血, 但這樣的初始血量是不合理的, 最小是 1
                // System.out.println("dp["+i + "][" + j + "]=" + dp[i][j]);
            }
        }
        

        return dp[0][0];
    }
}
/**
dp[2][2]=6
dp[2][1]=1
dp[2][0]=1
dp[1][2]=5
dp[1][1]=11
dp[1][0]=6
dp[0][2]=2
dp[0][1]=5
dp[0][0]=7

dp:
7 5 2
6 11 5
1 1  6

in 0,0
dp[i][j] = Math.min(dp[i+1][j] - dungeon[i][j], dp[i][j+1] - dungeon[i][j]);
dp[0][0] = Math.min(6 -(-2),  5 -(-2)) = Math.min(8, 7) = 7
 */

/*

T: O(mn)
S: O(mn)

dp[i][j]: is the health value(at least 1) which is alive for arriving m-1, n-1 ( so in m-1, n-1 it's 6)

一路弟推到0,0
(開始的初始值, so in m-1, n-1 終點(base case), it's 1 - dungeon[m-1][n-1], 因為都吃完藥水或扣完生命, 我們要讓他活著, 就是 1)

dp[i][j] = Math.min(dp[i+1][j] - dungeon[i][j], dp[i][j+1] - dungeon[i][j]);
for each dp, dp[i][j] = Math.max(dp[i][j], 1); can't let health < 1


跟一般 dp 走到右下角的狀態不一樣

一般的是 dp[i][j] = min(dp[i-1][j], dp[i][j-1])

這題是反過來的
dp[i][j] = min(dp[i+1][j], dp[i][j+1])
因為能不能繼續走下去取決於 是不是有足夠的血在一開始
所以 這一開始的血 要看 min(dp[i+1][j], dp[i][j+1])
選一個比較小的來走(可能是扣血的), 這樣一開始的血量可以給予少一點

所以變成從右下角開始算, 算到 (0,0), 就知道一開始需要多少血

但還要注意, 因為要算一開始血量, 所以要減掉  dungeon[i][j] or dungeon[i][j] 

dp[i][j] = Math.min(dp[i+1][j] - dungeon[i][j], dp[i][j+1] - dungeon[i][j]);
ex: 

(0,0) - (-3) -> 代表從 0,0 過來至少要 3滴血才能到 (0,1)

但這題不能 < 0 , 過程中
so dp[i][j] = Math.max(dp[i][j], 1);

最後 dp[0][0] 還要算扣掉自己的(或加血)
dp[0][0] = dp[0][0] - dungeon[0][0];
dp[0][0] = Math.max(dp[0][0], 1);
ex: dungeon[0][0] = -2

所以 dp[0][0] + 2 -> 才會是答案
*/
```

Last updated