174. 地下城游戏
一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。
我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);
其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快到达公主,骑士决定每次只向右或向下移动一步。
编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
-2 (K) -3 3
-5 -10 1
10 30 -5 (P)
若直接从左上角开始动态规划,那么只会根据当前附近格子进行判断,无法得知后续路径的信息,有可能做出不是最优的判断
所以我们换个思路,从右下角到左上角进行规划
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
rows = len(dungeon)
cols = len(dungeon[0])
dp = [[1] * cols for i in range(rows)]
for x in range(rows - 1, -1, -1):
for y in range(cols - 1, -1, -1):
if x == rows - 1 and y == cols - 1:
# 当是右下角元素
dp[x][y] = max(1, 1 - dungeon[x][y])
continue
elif x == rows - 1:
dp[x][y] = max(1, dp[x][y + 1] - dungeon[x][y])
continue
elif y == cols - 1:
dp[x][y] = max(1, dp[x + 1][y] - dungeon[x][y])
continue
else:
dp[x][y] = min(max(1, dp[x][y+1] - dungeon[x][y]), max(1, dp[x+1][y] - dungeon[x][y]))
return dp[0][0]
其中代码中间加了几段处理边界
我们也可以像官方题解,使用一个无穷大值来辅助边界条件判断
class Solution2:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
"""
使用极大值来赋予边界条件
:param dungeon:
:return:
"""
n, m = len(dungeon), len(dungeon[0])
BIG = 10**9
dp = [[BIG] * (m + 1) for _ in range(n + 1)]
dp[n][m - 1] = dp[n - 1][m] = 1
for i in range(n - 1, -1, -1):
for j in range(m - 1, -1, -1):
minn = min(dp[i + 1][j], dp[i][j + 1])
dp[i][j] = max(minn - dungeon[i][j], 1)
return dp[0][0]
特别地,dp[n-1][m-1]dp[n−1][m−1] 转移需要用到的 dp[n-1][m]dp[n−1][m] 和 dp[n][m-1]dp[n][m−1] 均为无效值,因此我们给这两个值赋值为 11。