返回

如何用动态规划算法秒杀“打家劫舍”专题三道题?

后端

动态规划:“打家劫舍”专题深究

前言

动态规划是一种强大的算法设计方法,在解决特定类型的优化问题时非常有效。在“打家劫舍”专题中,我们探讨了三个经典的动态规划问题,它们共同涉及一个核心问题:如何最大化收益,同时遵守特定的限制。

198. 打家劫舍

第一个问题是最基本的,小偷从街道一端开始,可以窃取任意房屋,但不能窃取相邻房屋。解决方案使用动态规划,按顺序计算每个房屋的最大收益,最终得到最大总收益。

213. 打家劫舍 II

第二个问题与第一个类似,但增加了限制:小偷不能从街道两端同时开始。解决方案将街道分为两部分,分别计算每个部分的最大收益,然后选择收益更大的部分。

337. 打家劫舍 III

第三个问题将场景从房屋转移到一棵二叉树。小偷只能窃取节点,不能窃取相邻节点。解决方案使用动态规划,从每个节点开始,递归计算以该节点为根的子树的最大收益,最终得到整个树的最大收益。

代码示例

198. 打家劫舍

def rob(nums):
    if not nums:
        return 0
    dp = [0] * len(nums)
    dp[0] = nums[0]
    dp[1] = max(nums[0], nums[1])
    for i in range(2, len(nums)):
        dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
    return dp[-1]

213. 打家劫舍 II

def rob(nums):
    if not nums:
        return 0
    if len(nums) == 1:
        return nums[0]
    part1 = nums[0:len(nums) - 1]
    part2 = nums[1:len(nums)]
    max1 = rob(part1)
    max2 = rob(part2)
    return max(max1, max2)

337. 打家劫舍 III

def rob(root):
    dp = {}
    def dfs(node):
        if not node:
            return 0
        if node in dp:
            return dp[node]
        val = max(node.val + dfs(node.left) + dfs(node.right), dfs(node.left) + dfs(node.right))
        dp[node] = val
        return val
    return dfs(root)

总结

“打家劫舍”专题中的问题展示了动态规划在解决最优化问题的强大功能。通过将问题分解为重叠子问题并按顺序求解,我们能够有效地计算出最佳解决方案。

常见问题解答

  • 动态规划适用于哪些类型的函数?
    动态规划适用于具有最优子结构和无后效性的函数。

  • 最优子结构是什么意思?
    最优子结构意味着问题整体的最优解可以由其子问题的最优解组成。

  • 无后效性是什么意思?
    无后效性意味着子问题的最优解不受其后继子问题的影响。

  • 动态规划算法的时间复杂度是多少?
    动态规划算法的时间复杂度通常是子问题数量的指数,但可以利用备忘录或动态规划表来优化复杂度。

  • 动态规划算法的空间复杂度是多少?
    动态规划算法的空间复杂度通常是子问题的数量,但也可以利用滚动数组来优化复杂度。