一些惡魔抓住了公主(P)並將她關在了地下城的右下角。地下城是由 M x N 個房間組成的二維網格。咱們英勇的騎士(K)最初被安置在左上角的房間裏,他必須穿過地下城並經過對抗惡魔來拯救公主。函數
騎士的初始健康點數爲一個正整數。若是他的健康點數在某一時刻降至 0 或如下,他會當即死亡。佈局
有些房間由惡魔守衛,所以騎士在進入這些房間時會失去健康點數(若房間裏的值爲負整數,則表示騎士將損失健康點數);其餘房間要麼是空的(房間裏的值爲 0),要麼包含增長騎士健康點數的魔法球(若房間裏的值爲正整數,則表示騎士將增長健康點數)。spa
爲了儘快到達公主,騎士決定每次只向右或向下移動一步。code
-2 (K) | -3 | 3 |
-5 | -10 | 1 |
10 | 30 | -5 (P) |
編寫一個函數來計算確保騎士可以拯救到公主所需的最低初始健康點數。遊戲
例如,考慮到以下佈局的地下城,若是騎士遵循最佳路徑 右 -> 右 -> 下 -> 下,則騎士的初始健康點數至少爲 7。leetcode
說明:io
騎士的健康點數沒有上限。table
任何房間均可能對騎士的健康點數形成威脅,也可能增長騎士的健康點數,包括騎士進入的左上角房間以及公主被監禁的右下角房間。class
根據題目的描述,這道題目是動態規劃無疑。可是如何創建遞推關係表達式呢?咱們令dp[i][j]
表示從(i,j)
出發到公主所在位置所需的最小的初始血量。那麼dp[0][0]+1
即爲救出公主所需的最小初始健康值。由於騎士的健康值在任什麼時候候都不能低於1,因此此處加了1。下面咱們分析下地推關係。咱們知道騎士能夠往下走或者往右走。若是\(dungeon[i][j] \ge dp[i][j+1]\),那麼由\((i,j)\rightarrow (i,j+1)\),由\(dungeon[i][j]\)提供的魔法球增長的健康值足夠騎士走到公主所在位置,不須要補給額外的能量。不然,則須要提供額外的健康值\(dp[i][j+1] - dungeon[i][j]\)才足以維持騎士的生命。由\((i,j)\rightarrow (i+1,j)\)也是同理,因而咱們有以下的表達im
\[ dp[i][j] = max(0, min(dp[i+1][j], dp[i][j+1])-dungeon[i][j]) \]
上式能夠保證在任什麼時候候\(dp[i][j]\ge 0\)。
下面給出代碼
class Solution { public: int calculateMinimumHP(vector<vector<int>>& dungeon) { const int m = dungeon.size(); const int n = dungeon[0].size(); int dp[m][n] = {}; dp[m-1][n-1] = dungeon[m-1][n-1] >= 0?0:-dungeon[m-1][n-1]; for(int i = m-1; i >= 0; --i) { for(int j = n-1; j >= 0; --j) { int ret = 0x3f3f3f3f; if(i < m - 1) ret = min(ret, dp[i+1][j] - dungeon[i][j]); if(j < n - 1) ret = min(ret, dp[i][j+1] - dungeon[i][j]); if (i < m - 1 or j < n - 1) dp[i][j] = max(0, ret); } } return dp[0][0] + 1; } };