• 代码随想录算法训练营Day48 (day47休息) | 动态规划(9/17) LeetCode 198.打家劫舍 213.打家劫舍II 337.打家劫舍III


    来到了新的一块内容:打家劫舍问题。

    第一题

    198. House Robber

    You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

    Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

    直觉告诉我,可能会涉及到一些贪心问题,为了保证利益最大,可能要找钱最多的地方优先动手。而当前房屋偷与不偷取决于,前一个房屋和前两个房屋是否被偷了。

    仍然可以利用动态规划五部曲来解决。

    1. 确定dp数组(dp table)以及下标的含义
      1. dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]
    2. 确定递推公式
      1. 决定dp[i]的因素就是第i房间偷还是不偷。如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
      2. 如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
    3. dp数组如何初始化
      1. 从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]
      2. 从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);
    4. 确定遍历顺序
      1. dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!

    5. 举例推导dp数组
    1. class Solution:
    2. def rob(self, nums: List[int]) -> int:
    3. if len(nums) == 0:
    4. return 0
    5. if len(nums) == 1:
    6. return nums[0]
    7. dp = [0] * len(nums)
    8. dp[0] = nums[0]
    9. dp[1] = max(nums[0], nums[1])
    10. for i in range(2, len(nums)):
    11. dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
    12. return dp[-1]

    第二题

    213. House Robber II

    You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night.

    Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

    和前一题比较,这道题变成环状了,需要考虑下面三个因素:

    对于一个数组,成环的话主要有如下三种情况:

    • 情况一:考虑不包含首尾元素
    • 情况二:考虑包含首元素,不包含尾元素
    • 情况三:考虑包含尾元素,不包含首元素
    1. class Solution:
    2. def rob(self, nums: List[int]) -> int:
    3. if not nums: # 如果没有房屋,返回0
    4. return 0
    5. if len(nums) == 1: # 如果只有一个房屋,返回该房屋的金额
    6. return nums[0]
    7. # 情况二:不抢劫第一个房屋
    8. prev_max = 0 # 上一个房屋的最大金额
    9. curr_max = 0 # 当前房屋的最大金额
    10. for num in nums[1:]:
    11. temp = curr_max # 临时变量保存当前房屋的最大金额
    12. curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额
    13. prev_max = temp # 更新上一个房屋的最大金额
    14. result1 = curr_max
    15. # 情况三:不抢劫最后一个房屋
    16. prev_max = 0 # 上一个房屋的最大金额
    17. curr_max = 0 # 当前房屋的最大金额
    18. for num in nums[:-1]:
    19. temp = curr_max # 临时变量保存当前房屋的最大金额
    20. curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额
    21. prev_max = temp # 更新上一个房屋的最大金额
    22. result2 = curr_max
    23. return max(result1, result2)

    第三题

     337. House Robber III

    The thief has found himself a new place for his thievery again. There is only one entrance to this area, called root.

    Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that all houses in this place form a binary tree. It will automatically contact the police if two directly-linked houses were broken into on the same night.

    Given the root of the binary tree, return the maximum amount of money the thief can rob without alerting the police.

    本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算

    1. class Solution:
    2. def rob(self, root: TreeNode) -> int:
    3. if root is None:
    4. return 0
    5. if root.left is None and root.right is None:
    6. return root.val
    7. # 偷父节点
    8. val1 = root.val
    9. if root.left:
    10. val1 += self.rob(root.left.left) + self.rob(root.left.right)
    11. if root.right:
    12. val1 += self.rob(root.right.left) + self.rob(root.right.right)
    13. # 不偷父节点
    14. val2 = self.rob(root.left) + self.rob(root.right)
    15. return max(val1, val2)

  • 相关阅读:
    QGraphicsView实现拖拽缩放
    Spring Security 单点登出
    现代密码学 - 知识点汇总
    安装Jenkins并在ruby中访问
    PostgreSQL的学习心得和知识总结(一百一十)|深入理解PostgreSQL数据库 日志格式jsonlog 的使用场景和实现原理
    ElasticSearch全文检索技术
    怎么使用PDF编辑器在PDF中插入图片?PDF插入图片的教程
    mysql单表记录的操作
    智能合约提款和转账错误
    【操作系统导论】机制:受限直接执行 | 中断处理 | 陷阱 | 协作方式 | 非协作方式 | 上下文切换
  • 原文地址:https://blog.csdn.net/Hanzq1997/article/details/132913528