LeetCode Dungeon Game

LeetCode解题之Dungeon Game


原题

公主被困在地牢(可以理解为矩阵或者二维数组)的右下角,骑士从左上角出发去救公主。骑士拥有一定的初始血量,如果途中他的血量少于等于0,那么营救失败。地牢中的每个格子都有一个数字,负数表示骑士收到了伤害,正数(可能为0)表示骑士回复了一定的血量。现在规定骑士只能向右或者向下前进,求骑士的初始血量至少为多少才能救出公主。

注意点:

  • 骑士的血量没有上限规定
  • 每格地牢都有数字,包括起点和终点。

例子:

输入:

-2 (K)   |  -3   |  3
-5     |  -10  |    1
10     |  30   |    -5 (P)

输出: 7 (右->右->下->下)

解题思路

一看就是典型的动态规划问题,不过这道题从左上角开始递推不好推,因为要考虑两个因素,一是剩余的血越多越好(血量多可以使得以后的路程中血量更加高一点),行进过程中血量最低值越高越好(这个直接影响到结果),这两个因素都要考虑,且相互影响。其实我们可以从右下角开始推,dp表示如果从该格子出发,最少要多少初始血量。此时的递归关系为:

dp[j][i] = max(min(dp[j + 1][i], dp[j][i + 1]) - dungeon[j][i], 1)

我们来梳理一下这个关系,在(j,i)位置出发最少的初始血量dp[j][i],而从它右边的格子出发最少的初始血量为dp[j][i+1],从下边的格子出发的最少血量为dp[j+1][i]。为了使dp[j][i]尽可能小,我们要使得dp[j][i] + dungeon[j][i] = min(dp[j + 1][i], dp[j][i + 1]。同时dp[j][i]又必须大于0。

AC源码

class Solution(object):
    def calculateMinimumHP(self, dungeon):
        """
        :type dungeon: List[List[int]]
        :rtype: int
        """
        n = len(dungeon)
        m = len(dungeon[0])
        dp = [[0 for __ in range(m)] for __ in range(n)]
        dp[-1][-1] = 1 if dungeon[-1][-1] > 0 else 1 - dungeon[-1][-1]
        for i in range(m - 2, -1, -1):
            dp[-1][i] = max(1, dp[-1][i + 1] - dungeon[-1][i])
        for j in range(n - 2, -1, -1):
            dp[j][-1] = max(1, dp[j + 1][-1] - dungeon[j][-1])
        for j in range(n - 2, -1, -1):
            for i in range(m - 2, -1, -1):
                dp[j][i] = max(min(dp[j + 1][i], dp[j][i + 1]) - dungeon[j][i], 1)
        return dp[0][0]


if __name__ == "__main__":
    assert Solution().calculateMinimumHP([[-2, -3, 3], [-5, -10, 1], [10, 30, -5]]) == 7

欢迎查看我的Github (https://github.com/gavinfish/LeetCode-Python) 来获得相关源码。

你可能感兴趣的:(LeetCode,LeetCode,Python,LeetCode,Python,算法,动态规划)