首页 > 其他分享 >【动态规划】打家劫舍

【动态规划】打家劫舍

时间:2023-01-16 16:58:36浏览次数:49  
标签:动态 nums int rob self 打家劫舍 规划 节点 dp

目录

打家劫舍

力扣上打家劫舍相关的题目如下:

序号 题目 区别
1 198. 打家劫舍 不能偷窃相邻的房间
2 213. 打家劫舍 II 房间连成环
3 337. 打家劫舍 III 房间组成一棵二叉树

应用

应用1:Leetcode.198

题目

198. 打家劫舍

分析

设 \(dp[i]\) 表示前 \(i\) 个房间能获取的最大金额,设房间数量为 \(n\),那么, \(0 \le i \le n\)。

边界条件

当只有一个房间时,显然最大收益,就是选择偷窃该房间所所得的收益,即

\[dp[0] = nums[0] \]

状态转移

当 \(i \ge 1\) 时,对于第 \(i\) 个房间\(nums[i]\),有两种选择:

  • 不选择偷窃这个房间,那么,可以获取的最大金额就是 \(dp[i - 1]\);
  • 选择偷窃这个房间,那么,可以获取的最大金额就是 \(dp[i - 2] + nums[i]\);

综上,能获取的最大金额就是上述两种情况的最大值,即状态转移方程:

\[dp[i] = \max(dp[i - 1], \ dp[i - 2] + nums[i]), \quad i \ge 1 \]

代码实现

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [0] * n
        dp[0] = nums[0]
        for i in range(1, n):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
        return dp[n - 1]

对其压缩状态,优化后的实现:

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 1:
            return nums[0]
        a = nums[0]
        b = max(nums[1], nums[0])
        c = b
        for i in range(2, n):
            c = max(b, a + nums[i])
            a = b
            b = c
        return c

应用2:Leetcode.213

题目

213. 打家劫舍 II

分析

设 \(dp[i]\) 表示前 \(i\) 个房间能获取的最大金额,设房间数量为 \(n\)。

题目中的限制条件,可以等价于:

  • 若偷窃了第一个房间,就不能偷窃最后一个房间,即可以偷窃房间的范围为:\(nums[0], \ \cdots, \ nums[n -2]\);
  • 若没有偷窃第一个房间,就可以偷窃最后一个房间,即可以偷窃房间的范围为:\(nums[1], \ \cdots, \ nums[n -1]\)。

因此,我们只需要针对上述两种情况,利用前面一道题的分析结果,分别计算两种情况的最大值,最后,再选择最优解即可。

代码实现

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 1:
            return nums[0]
        return max(self.rob_money(nums[1:]), self.rob_money(nums[:n - 1]))

    def rob_money(self, nums:List[int]) -> int:
        n = len(nums)
        dp = [0] * n
        dp[0] = nums[0]
        for i in range(1, n):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
        return dp[n - 1]

应用3:Leetcode.337

题目

337. 打家劫舍 III

分析

由于,我们一般采用递归的方式遍历一棵树,所以,求解树上的动态规划时,通常采用自顶向下的方式,通过分解子问题的方式求解。

对于任意一个节点,我们注意到,它都会有两种状态:选择当前节点或者不选择节点,所以,递归的过程中,为了表示两种状态,我们定一个函数 do_rob(node: TreeNode),它返回一个元组 (profit1, profit2)表示对于节点 \(node\) 可以获取的收益,其中元组的值 \(profit1\)、\(profit2\) 分别表示选择该节点的最大收益不选择该节点的最大收益

对于任意一个节点 \(node\),我们都有两种选择:选择当前节点或者不选择节点,因此:

  • 选择当前节点,那么收益就是当前节点的值,加上不选择两个子节点的收益;
  • 不选当前节点,那么收益就是两个子节点的收益之和,其中,子节点的最大收益等于选择子节点的收益和不选择子节点的收益的最大值;

这里,我们选择通过后序遍历的方式计算每个节点收益,即在离开当前节点的时候,计算当前节点的收益。

代码实现

class Solution:
    def rob(self, root: TreeNode) -> int:
        if not root:
            return 0
        profit = self.do_rob(root)
        return max(profit[0], profit[1])

    def do_rob(self, root: TreeNode) -> Tuple[int, int]:
        if not root:
            return 0, 0

        # 左侧子树的收益
        left = self.do_rob(root.left)
        # 右侧子树的收益
        right = self.do_rob(root.right)

        # 选择当前节点的最大收益:选择了当前节点,左右节点都不能选择
        profit1 = root.val + left[1] + right[1]
        # 不选择当前节点的最大收益:左右子节点的最大收益之和
        profit2 = max(left[0], left[1]) + max(right[0], right[1])
        return profit1, profit2

标签:动态,nums,int,rob,self,打家劫舍,规划,节点,dp
From: https://www.cnblogs.com/larry1024/p/17055009.html

相关文章

  • elementUI 的el-table中使用了动态列出现高度塌陷
    使用动态列的表格初次加载时出现塌陷解决方法首先检查你的布局是否有问题,具体方法是页面生成后发生高度塌陷再使用控制台缩放页面大小时,引起页面重绘后高度恢复正常,这时......
  • 记一次线上动态数据迁移经历
    一、背景平台重构上线以后需要面临新老服务切流以及新老数据库数据迁移。二、目标做到接近零停机时间完成生产环境数据迁移,保证生产环境数据零丢失,并且迁移完成后新服......
  • Java动态代理机制
    概念代理模式是Java当中最常用的设计模式之一。其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等......
  • iview daterange动态设置可选范围
    需求:iview中日期选择控件daterange动态设置日期可选范围,如选择一个月内。当用户选择第一个日期后,往前、往后,都只能选择一个月内的日期。思路:1、当用户选中第一个日期时,......
  • linux加载动态库.so的3种方法
        昨天同事联系我,他部署新版本的MS软件提示找不到动态库。但是他能找到这个动态库文件,但不知道如何加载。这样的问题对于我来说是个再简单不过的问题,但对于一个新......
  • python和C++调用动态库
    python和C++调用动态库python和C++相互调用动态库的方法有4种:python调用C/C++编译的动态库python调用python编译的动态库C/C++调用python编译的动态库C/C++调用C/C++......
  • Java对象转JSON动态设置字段
    需求User类:@DatapublicclassUser{ privateStringname; privateIntegerage;}序列化成JSON时,处理动态增加一个sex字段{ "name":"张三", "age":20, "sex......
  • 如何动态修改属性文件×××.properties的某些内容
    我们在项目中可以把一些属性配置到×××.properties中,比如数据库连接信息。现在问题来了,我的属性文件中有一些值是需要根据后台得到的数据来动态改变的,请问这个要怎么实现......
  • 使用动态输出打印内核的DEBUG信息
    简介printk()是很多嵌入式开发者喜欢用的调试手段之一,但是,使用printk()每次都要重新编译内核,很不方便。使用动态输出在不需要重新编译内核的情况下,方便的打印出内核的debu......
  • 【转】PageOffice动态生成Word文件并转换为PDF
    说明:PageOffice是客户端插件,做不到纯后台调用把word转为pdf。但是pageoffice的FileMaker对象可以实现不在客户端打开文件直接转换文件为pdf并保存到服务器端,看起来跟服务器......