day39 动态规划07

64 阅读3分钟

198. 打家劫舍

文章讲解

思路:

dp定义: dp(i)表示从i个房子开始抢到的最大值
状态转移方程:dp(i) = max(dp(i+1), nums[i]+dp(i+2)) # 不抢,抢下一家;抢当前,之后抢i+2
base case:走过了最后一间房子,n>=len(nums),没得抢了,金额为0
class Solution:
    def rob(self, nums: List[int]) -> int:
        # 处理base case 0 1 2 的情况
        n = len(nums) 
        if n == 0:
            return 0
        elif n==1:
            return nums[0]
        elif n==2:
            return max(nums[0], nums[1])

        # dp[i]表示抢到第i个房子的最大价值
        dp = [0] * n
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])


        # 迭代1~ n-1
        for i in range(2, n):
            dp[i] = max(nums[i] + dp[i-2], dp[i-1])
        return dp[n-1]
class Solution:
    def rob(self, nums: List[int]) -> int:
        # 处理base case 0 1 2 的情况
        n = len(nums) 
        if n == 0:
            return 0
        elif n==1:
            return nums[0]

        prevMax = 0  # 上一个房屋的最大金额 (相当于n-2)
        curMax = 0  # 当前房屋的最大金额 (相当于n-1)
        for num in nums:
            temp = curMax
            curMax = max(curMax, prevMax+num)
            prevMax = temp
        return curMax

213. 打家劫舍 II

文章讲解

思路:

prev cur next(num)
当道next时有两种情况:
1num,那就要加n-2的价值: prev+num
2 不抢num,那就上一个cur的
curMax = max(curMax, prevMax+num)
class Solution:
    def rob(self, nums: List[int]) -> int:
        # base case
        n = len(nums)
        if n == 0:
            return 0
        elif n == 1:
            return nums[0]
    

        # 不抢最后一个
        prevMax = 0  # 上一个房屋的最大金额
        curMax = 0  # 当前房屋的最大金额
        for num in nums[:-1]:
            temp = curMax
            curMax = max(curMax, prevMax + num)
            prevMax = temp
        result1 = curMax

        # 不抢开头
        prevMax = 0
        curMax = 0
        for num in nums[1:]:
            temp = curMax
            curMax = max(curMax, prevMax + num)
            prevMax = temp
        result2 = curMax
        return max(result1, result2)

337. 打家劫舍 III

文章讲解

思路1: 暴力递推(后序)

1 偷父节点,接着偷孙子节点
2 不偷父节点,考虑偷孩子节点

然后求两种方法的最大值
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        if not root.left and not root.right:
            return root.val # 叶子节点

        # 偷父亲节点
        val1 = root.val
        if root.left:
            val1 += self.rob(root.left.left) + self.rob(root.left.right)
        if root.right:
            val1 += self.rob(root.right.left) + self.rob(root.right.right)

        # 不偷
        val2 = self.rob(root.left) + self.rob(root.right)

        return max(val1, val2)

思路2: 带备忘录的递推

用memo来记录已经算过的节点,避免反复计算某个节点。
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        
        self.memo = {} # 记录节点到这个节点最大的盗窃的数额

        return self.rob_helper(root)


    def rob_helper(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        if not root.left and not root.right:
            return root.val # 叶子节点

        # 判断是否在memo中
        if root in self.memo:
            return self.memo[root]

        # 偷父亲节点
        val1 = root.val
        if root.left:
            val1 += self.rob_helper(root.left.left) + self.rob_helper(root.left.right) # 注意调用rob_helper!
        if root.right:
            val1 += self.rob_helper(root.right.left) + self.rob_helper(root.right.right)

        # 不偷
        val2 = self.rob_helper(root.left) + self.rob_helper(root.right)

        self.memo[root] = max(val1, val2)
        return  self.memo[root]

思路3: 树形dp

树形dp
dp数字定义:<a,b> a表示不偷当前节点的子树最大盗窃金额;b表示偷当前节点的最大金额
robTree:某个节点root为根的子树的 最大盗窃金额,返回<a,b>
初始化:空时候返回00

后序位置迭代
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        result = self.robTree(root)
        return max(result)

    # 后序遍历, 要通过递归函数的返回值来做下一步计算
    def robTree(self, root: Optional[TreeNode]) -> int:
        if not root:
            return [0,0] # 偷的最大值,不偷当前的最大值

        # 先计算左右的值
        left = self.robTree(root.left)
        right = self.robTree(root.right)

        # 不偷当前
        val1 = max(left[0], left[1]) + max(right[0], right[1])

        # 偷当前节点
        val2 = root.val + left[0] + right[0]
        return (val1, val2)