代码随想录算法训练营 Day 34

1 minute read

Published:

动态规划五部曲

  1. 确定dp数组以及下标的含义
  2. 确定递推公式
  3. 初始化dp数组
  4. 确定遍历顺序
  5. 举例推导dp数组

第5步随着输入不同而变化,因此在此省去

Q1. LeetCode 198

前四步:

  1. 使用一维的dp数组,dp[i]表示打劫到第i个房子最大的金额
  2. 递推公式:1. 要打劫当前房子,那么就不能打劫前面一个,因此$dp[i] = dp[i - 2] + money[i]$。2. 不打劫当前房子,那么最大金额就是上一套房子的最大金额,$dp[i] = dp[i - 1]$。因此$dp[i]=max(dp[i-2] + money[i], dp[i-1])$
  3. 初始化:从递推公式中,我们需要dp数组最开始两个的值,因此分别对其初始化。$dp[0] = money[0]$因为当前只能打劫一套,$dp[1] = max(money[0], money[1])$因为从两套中选择钱多的那一套
  4. 遍历顺序:从前向后遍历
class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        if len(nums) == 1:
            return nums[0]
        
        n = len(nums)
        dp = [0] * n

        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])

        for i in range(2, n):
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
        
        return dp[-1]

复杂度分析

时间复杂度:O($n$) 空间复杂度:O($n$)

Q2. LeetCode 213

因为房子排列成环形,所以第一个和最后一个不能同时打劫。因此分两种情况讨论:1. 不考虑最后一套,前面的房子还是和Q1一样的思路;2. 不考虑第一套,后面的房子还是和Q1一样的思路

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        if len(nums) == 1:
            return nums[0]
        
        n = len(nums)
        # 不考虑最后一套房子
        dp = [0] * n

        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])

        for i in range(2, n - 1):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

        # 不考虑第一套房子
        dp1 = [0] * n

        dp1[1] = nums[1]
        dp1[2] = max(nums[1], nums[2])

        for i in range(3, n):
            dp1[i] = max(dp1[i - 1], dp1[i - 2] + nums[i])

        return max(dp[-2], dp1[-1])

复杂度分析

时间复杂度:O($n$) 空间复杂度:O($n$)

Q3. LeetCode 337

树形动态规划再理解

class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        dp = self.traverse(root)
        
        return max(dp)

    def traverse(self, node):
        if not node:
            return (0, 0)
        
        left = self.traverse(node.left)
        right = self.traverse(node.right)

        not_rob_val = max(left[0], left[1]) + max(right[0], right[1])

        rob_val = node.val + left[0] + right[0]

        return (not_rob_val, rob_val)

复杂度分析

时间复杂度:O($n$) 空间复杂度:O($logn$)