M×N 地下城,騎士從左上角 (0,0) 出發,只能向右或向下移動,目標到達右下角 (M-1,N-1) 救公主。每個格子有數值:負數扣血,正數加血,0 無影響。騎士血量降至 0 或以下即死亡。求騎士最少需要多少初始血量才能成功救出公主。
定義 dp[i][j] 為從 (i,j) 到達終點所需的最小血量(進入該格子前的血量)。逆向思考:從終點反推起點。
dp[M-1][N-1] = max(1, 1 - dungeon[M-1][N-1])。dp[M-1][j] = max(1, dp[M-1][j+1] - dungeon[M-1][j])。dp[i][N-1] = max(1, dp[i+1][N-1] - dungeon[i][N-1])。dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])。只需一維陣列 dp[N],從右下向左上滾動更新,空間 O(N)。
class Solution {
public:
int calculateMinimumHP(vector>& dungeon) {
int m = dungeon.size(), n = dungeon[0].size();
vector dp(n, INT_MAX);
dp[n-1] = 1;
for (int i = m-1; i >= 0; --i) {
for (int j = n-1; j >= 0; --j) {
if (i == m-1 && j == n-1) {
dp[j] = max(1, 1 - dungeon[i][j]);
} else if (i == m-1) {
dp[j] = max(1, dp[j+1] - dungeon[i][j]);
} else if (j == n-1) {
dp[j] = max(1, dp[j] - dungeon[i][j]);
} else {
dp[j] = max(1, min(dp[j], dp[j+1]) - dungeon[i][j]);
}
}
}
return dp[0];
}
};
逆向 DP 避免了正向決策的不確定性。滾動陣列優化空間。注意血量至少為 1。