力扣LeetCode174.地下城游戏及Python源码实现

本题为LeetCode题库中第174题地下城游戏,难度困难,题目请参考这里。

一道比较困难DP题目,其中的难点在于如何定义状态转移方程,所以在这里记录一下。这道题刚开始做一点思路都没有,但是通过解读别人的思想一下就明白了。

力扣LeetCode174.地下城游戏及Python源码实现_第1张图片首先一看到这道题,思路肯定是DP来做:直觉从左上出发往右或者往下走这样去找规律,从而得到dp的状态转移方程。在这里建议先把这道题做了。本题有点特殊,如果没有 至少 这个条件从左上往右下推找规律得到状态转移方程的思路应该是可以的。这里我们不去讨论从左上往右下的思路,直接想从右下往左上推。

首先,我们不去考虑二维的复杂情况,考虑二维数组为1*n或者n*1的情况。那么有:力扣LeetCode174.地下城游戏及Python源码实现_第2张图片

解释一下,比如最后一点是-5那么dp数组在这个位置的数值至少为6才能满足6-5>=1,往前为2那么这个位置的dp数组至少为6-2=4,也就是说在这个位置是4加上2在到最后-5也能满足骑士在每个位置的生命值都是>=1的。那么dp数组每个位置的数值就是max(1, dp[0][i+1]-dungeon[o][i]),因为骑士在每个点的数值都要>=1,所以不能为负至少都要为1。好,理解了一行或者一列的情况,下面我们来考虑二维的复杂情况

力扣LeetCode174.地下城游戏及Python源码实现_第3张图片

解释一下,我们对最后一列和最后一行的dp数组进行特殊处理,因为它只和下一行的元素和下一列的元素有关。所以这里i和j从n-2和m-2开始。再来看转移方程,首先看dp_min代表的是当前位置dp[i][j]要从dp[i-1][j]或者dp[i][j-1]向左或者向上移动得到,当然我们选择其中最小的值,因为题目中要求的是求初始最小的生命值。我们选择这个最小的生命值然后和dungeon[i][j]做减法,上面分析了要从该值和1中选择一个max作为dp[i][j]的值,至此这道题的整个算法的核心思想就讲完了。

总结一下这道题:一道dp困难题目,虽然是hard题目,但是做完了感觉没那么难,但是开始不知道从后往前推那就麻烦了。所以dp这种题一定要从多个角度考虑,这个只能见多识广才能有经验。代码如下,可以把变量名称定义简化一下。

class Solution:
    def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
        dp_min = 0
        dp = [[1 for _ in range(len(dungeon[0]))] for _ in range(len(dungeon))] #定义dp矩阵
        dp[-1][-1] = max(1, 1-dungeon[-1][-1]) #把最后一个元素定义好
        for i in range(len(dungeon)-2, -1, -1):
            dp[i][len(dungeon[0])-1] = max(1, dp[i+1][len(dungeon[0])-1]-dungeon[i][len(dungeon[0])-1]) #处理最后一行
        for j in range(len(dungeon[0])-2, -1, -1): #处理最后一列
            dp[len(dungeon)-1][j] = max(1, dp[len(dungeon)-1][j+1]-dungeon[len(dungeon)-1][j])
        for i in range(len(dungeon)-2, -1, -1): #根据状态转移方程更新dp数组
            for j in range(len(dungeon[0])-2, -1, -1):
                dp_min = min(dp[i+1][j], dp[i][j+1])
                dp[i][j] = max(1, dp_min-dungeon[i][j])
        return dp[0][0] #返回dp开始的头元素也就是至少需要的生命值

 

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