leetcode筆記系列 174 地下城遊戲
題目描述如下:
一些惡魔抓住了公主(P)並將她關在了地下城的右下角。地下城是由 M x N 個房間組成的二維網格。我們英勇的騎士(K)最初被安置在左上角的房間裏,他必須穿過地下城並通過對抗惡魔來拯救公主。
騎士的初始健康點數為一個正整數。如果他的健康點數在某一時刻降至 0 或以下,他會立即死亡。
有些房間由惡魔守衛,因此騎士在進入這些房間時會失去健康點數(若房間裏的值為負整數,則表示騎士將損失健康點數);其他房間要麽是空的(房間裏的值為 0),要麽包含增加騎士健康點數的魔法球(若房間裏的值為正整數,則表示騎士將增加健康點數)。
為了盡快到達公主,騎士決定每次只向右或向下移動一步。
編寫一個函數來計算確保騎士能夠拯救到公主所需的最低初始健康點數。
例如,考慮到如下布局的地下城,如果騎士遵循最佳路徑 右 -> 右 -> 下 -> 下
,則騎士的初始健康點數至少為 7。
-2(K) | -3 | 3 |
-5 | 10 | 1 |
-9 | -10 | -5(P) |
說明:
-
騎士的健康點數沒有上限。
- 任何房間都可能對騎士的健康點數造成威脅,也可能增加騎士的健康點數,包括騎士進入的左上角房間以及公主被監禁的右下角房間。
解題思路:
這是一道比較典型的動態規劃問題。一開始我想的比較復雜,因為需要考慮騎士到達每一個房間與惡魔打鬥的時候,確保不會掛,也就是其體力大於零。後來網上參考了其他大佬寫的博客,發現,其實從公主的位置開始倒推,也就是按照從下往上、從左往右的方式處理,會更好一些。因為我們不知道騎士在起點處到底需要多少體力,但是我們知道,從某個房間,去下一個房間需要多少體力。所以,還是拿題目給的例子來分析,這是一個3*3的地牢,騎士最後所到達的有公主的房間,需要消耗5點體力。在此之前,如果騎士到達公主左邊的房間,需要10+5=15點體力,才能順利解救公主;或者,騎士到達的是公主上面的房間,則一共需要4點體力。我們再往前看,假設騎士剛進入了地牢正中間的這個房間。騎士在該房間不消耗體力,並撿到了10點體力。之後騎士將面臨兩個選擇:1.去下面的房間,然後消耗15點體力解救公主;2.去右邊的房間,消耗4點體力。我們肯定選2對吧。由於中間的房間可以撿到10點體力,之後再消耗4點體力,多剩了6點體力。也就是說,騎士從正中間的房間走到最後,相當於是不用消耗體力的。
解釋到這裏,遞推公式就出來了:hp[i][j] = max(0,min(hp[i+1][j], hp[i][j+1])-dungeon[i][j]),其中hp[i][j]指從牢中房間ij開始,走到最後所需要消耗的體力(大於等於0)。dungeon[i][j]表示在房間ij中需要消耗的體力。遞推到最後,hp[0][0]的值就表示,從地牢左上角到地牢右下角所需要消耗的最小體力。因此,騎士進入地牢,保證有1+hp[0][0]的體力,就ok了。
下面上代碼:
1 public int calculateMinimumHP1(int[][] dungeon) { 2 3 intn = dungeon.length; 4 int m = dungeon[0].length; 5 int[][] hp = new int[n][m]; 6 for(int i=n-1;i>=0;i--){ 7 for(int j=m-1;j>=0;j--){ 8 if(i==n-1&&j==m-1){ 9 hp[i][j]=Math.max(0, -dungeon[i][j]); 10 continue; 11 } 12 int right = (j==m-1)?Integer.MAX_VALUE:hp[i][j+1]; 13 int down = (i==n-1)?Integer.MAX_VALUE:hp[i+1][j]; 14 hp[i][j] = Math.max(0, Math.min(right,down)-dungeon[i][j]); 15 } 16 } 17 return hp[0][0]+1; 18 }
註意第8-13用來處理邊界問題。其實,還可以繼續優化,只用以為數組hp,就可以達到效果:
1 public int calculateMinimumHP2(int[][] dungeon) { 2 3 int n = dungeon.length; 4 int m = dungeon[0].length; 5 int[] db = new int[m]; 6 for(int i=n-1;i>=0;i--){ 7 for(int j=m-1;j>=0;j--){ 8 if(i==n-1&&j==m-1){ 9 db[j]=Math.max(0, -dungeon[i][j]); 10 continue; 11 } 12 int right = (j==m-1)?Integer.MAX_VALUE:db[j+1]; 13 int down = (i==n-1)?Integer.MAX_VALUE:db[j]; 14 db[j] = Math.max(0, Math.min(right,down)-dungeon[i][j]); 15 } 16 } 17 return db[0]+1; 18 }
最後,本人水平有限。如果遺漏,歡迎進行指正~同時歡迎留言進行討論,謝謝~
leetcode筆記系列 174 地下城遊戲