动态规划之打家劫舍问题I,II,III

LeetCode 198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400

动态规划
  1. 用 d[i] 表示偷前 i 辆车电瓶可以获得的最大价值,那么对于第 i 辆车来说,有两种选择。

  2. 如果偷第i 辆车的电瓶,那么第i-1辆车电瓶就不能偷了,能获得的最大价值就是nums[i] + d[i-2]。

  3. 如果不偷第 i 辆车的电瓶,那么最大价值就等价于偷前 i-1 辆车的电瓶能获得的最大价值d[i-1]。

  4. 所以最终取两者最大值即可:max(d[i-1],d[i-2]+nums[i])

  5. 可以发现,每次计算其实只要用到前两个元素,所以每次维护最后两个值即可,可以将空间优化到常数空间。

class Solution:
    def rob(self, nums: List[int]) -> int:
        fisrt, second, max_now = 0, 0, 0
        for i in range(len(nums)):
            max_now = max(second, fisrt+nums[i])
            fisrt = second
            second = max_now
        return max_now
打家劫舍II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:
输入:nums = [0]
输出:0

提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000

动态规划

这里表示第一个和最后一个房屋只能选择一个去偷,还是利用动态规划方法去调节,这里注意选择即可:
因此可以把此环状排列房间问题约化为两个单排排列房间子问题:

  1. 在不偷窃第一个房子的情况下(即 nums[1:]nums[1:]),最大金额是p1

  2. 在不偷窃最后一个房子的情况下(即nums[:n−1]),最大金额是 p2

  3. 综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2)。

class Solution:
    def rob(self, nums: List[int]) -> int:
        def my_rob(nums):
            first,second,max_now = 0,0,0
            for i in range(len(nums)):
                max_now = max(second,first+nums[i])
                first = second
                second = max_now
            return max_now
        
        return max(my_rob(nums[1:]), my_rob(nums[:len(nums)-1])) if len(nums)!=1 else nums[0]
337. 打家劫舍 III

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:
输入: [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:
输入: [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

题解,动态规划
  1. 一棵树上偷窃了,做法还是一样。对于结点 r 来说,我们还是分为偷和不偷两种情况。

  2. 如果偷的话,它的左右儿子就不能偷了,所以最大价值就是左儿子L不偷的最大价值,加上右儿子不偷的最大价值,再加上 r 的价值。

  3. 而如果不偷的话,最大价值就是左儿子偷或不偷的最大价值,加上右儿子偷或不偷的最大价值。

  4. 因为需要用到儿子结点偷和不偷两个价值,所以需要在 dfs 时返回两个值,0代表不偷左或右结点,1代表偷左或右结点

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rob(self, root: TreeNode) -> int:
        def dfs(root):
            if not root:
                return [0,0]
            l = dfs(root.left)
            r = dfs(root.right)
            #不偷根节点,选择左/右结点偷或者不偷的最大值
            r0 = max(l[0], l[1]) + max(r[0], r[1])
            #偷根节点,不偷左右节点
            r1 = l[0] + r[0] + root.val
            return [r0, r1]
        
        res = dfs(root)
        return max(res[0], res[1])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值