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 (negativeintegers) 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) |
Note:
- 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.
-----------------------------------------
这题对脑子上的坑特别多:
1. 这个路径不对称,假设K和P的位置颠倒,结果完全不一样,很容易举例子
2. 假设从左上出发,到达(i,j)时,有两个状态,一是这条路径上最大的分、二是当前的分,从(i,j)到(i+1,j)或者(i,j+1)时,这两个分可能在这两个方向上都刷新,状态急剧增多,脑子进入大坑。。。
针对2采用逆向思维的方式,假设dp[i][j]表示到达前(i,j)最少体力值是多少,那么从dp[rows-1][cols-1]开始逆向递推。。。after表示被这格子消耗后至少要保留多少体力,才够满足下游的消耗,当然after >= 1,所以从下游向上游dp[i][j]反推:
from typing import List
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
rows,cols = len(dungeon),len(dungeon[0]) if dungeon else 0
dp = [[0 for j in cols] for i in rows]
if rows == 0 or cols == 0:
return 0
for i in range(rows)[::-1]:
for j in range(cols)[::-1]:
after = 1
if (i == rows-1 and j == cols-1):
after = 1
elif (i == rows-1):
after = dp[i][j+1]
elif (j == cols-1):
after = dp[i+1][j]
else:
after = min(dp[i][j+1],dp[i+1][j])
#after表示被(i,j)位置的格子消耗后至少要保留多少体力(当然after>=1),才够下游继续消耗,然后向上游反推
if (after <= dungeon[i][j]):
dp[i][j] = 1
elif (dungeon[i][j] < 0):
dp[i][j] = after-dungeon[i][j]
else:
dp[i][j] = after-dungeon[i][j]
return dp[0][0]
s = Solution()
print(s.calculateMinimumHP([[-1,1]]))
print(s.calculateMinimumHP([[-2,-3,3],
[-5,-10,1],
[10,30,-5]]))