首页 > 其他分享 >树形DP学习笔记(二):打家劫舍III & 监控二叉树

树形DP学习笔记(二):打家劫舍III & 监控二叉树

时间:2024-12-31 18:27:22浏览次数:8  
标签:right root self 担保 二叉树 III 节点 DP left

参考:

树形 DP:打家劫舍III【基础算法精讲 24】_哔哩哔哩_bilibili

树形 DP:监控二叉树【基础算法精讲 25】_哔哩哔哩_bilibili

ps:笔记中的代码按本人理解整理,重思路,对比原视频中的代码稍有改动

往期:

树形DP学习笔记(一):树的路径问题-CSDN博客

状态机DP学习笔记-CSDN博客

【如果笔记对你有帮助,欢迎关注&点赞&收藏,收到正反馈会加快更新!谢谢支持!】

上期树的每个节点只传递一个值(如深度、对应数值等)

这期树的每个节点需要传递多个值(每个节点有多种可能情况,需要分类讨论)

但是核心思路不变

题目4: 打家劫舍III

337. 打家劫舍 III - 力扣(LeetCode)

  • 思路:每个节点有偷和不偷两种情况,对应的金额都需要传递(因为都需要用到,传递的是这棵树的金额)
  • 拆解:
    • 当前节点:左节点不偷,右节点不偷
      【树的金额 = 当前节点金额 + 不偷左节点时左子树的金额 + 不偷右节点时右子树的金额】
    • 不偷当前节点:左节点偷/不偷都可以,右节点偷/不偷都可以
      【树的金额 = 左子树金额max{偷左节点时,不偷左节点时}+ 右子树金额max{偷右节点时,不偷右节点时}
  • 代码
    # 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 dfs(self, root: Optional[TreeNode]):
            if not root:
                return (0,0) # 节点为None,返回0,确定递归边界
    
            # w表示偷,wo表示不偷
            # 左右节点都需要传递回这两种情况对应的金额,给当前节点使用【这里root指当前节点,不是树的根节点】
            w_l, wo_l = self.dfs(root.left) # 返回左节点选择偷和选择不偷时左子树的金额
            w_r, wo_r = self.dfs(root.right) # 返回右节点选择偷和选择不偷时右子树的金额
            # 当前节点处理
            # 偷当前节点:当前节点金额 + 不偷左节点时,左子树的金额 + 不偷右节点时,右子树的金额
            # 不偷当前节点:左子树金额(偷或不偷左节点情况哪个大取哪个)+ 右子树金额(偷或不偷右节点情况哪个大取哪个)
            w_root = wo_l + wo_r + root.val 
            wo_root = max(w_l, wo_l) + max(w_r, wo_r)
            return (w_root, wo_root)
    
        def rob(self, root: Optional[TreeNode]) -> int:
            return max(self.dfs(root)) # 偷root和不偷root的情况哪个大取哪个
    
          
    

题目5: 监控二叉树

968. 监控二叉树 - 力扣(LeetCode)

  • 变化:【考虑单元】 “当前节点,左节点,右节点”  → “当前节点,父节点,左节点,右节点”

  • 思路:当前节点要监控到有三种办法(如下),所对应的数量都要传递
  • 拆解:
    • 保证当前节点被监控到,有三个方法:
      1. 当前节点摄像头。 数量 = 左值 + 右值 + 1
      2. 当前节点不装摄像头,其父节点装。数量 = 左值 + 右值
      3. 当前节点不装摄像头,其子节点装。数量 = 左值 + 右值
    • 更详细的解释版本:
      【这里要理解成“担保”,意思用下面哪个方法来“担保”当前节点是被监控的,而不是安装情况的分类讨论】
      1. 当前节点摄像头【自己“担保”】:左节点的担保情况可以是(1)自己“担保” (2)靠父节点“担保” (3)靠子节点“担保”。右节点同理。 因为要求最小数量,所以要取所有情况的最小值。
      2. 当前节点不装摄像头,其父节点装【父节点“担保”】:左节点的担保情况可以是:(1)自己“担保”  (2)靠子节点“担保”。不能靠父节点担保了,因为父节点没装。右节点同理。
      3. 当前节点不装摄像头,其子节点装【子节点“担保”】:左节点和右节点必须有一个来担保。两种情况:
        1. 选左节点“担保”时, 右节点的担保情况可以是(1)自己”担保“ (2)子节点”担保“。
        2. 选右节点“担保”时, 左节点的担保情况可以是(1)自己”担保“ (2)子节点”担保“。
  • 代码
  • # 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 minCameraCover(self, root: Optional[TreeNode]) -> int:
            
            def dfs(root):  
                if not root:
                    return (float('inf'), 0, 0) #第一个要初始化为无穷大的原因写在最后,先看下面递归过程
                
                # r: 装当前节点(root), p:装父节点(parent), c装子节点(child) 【这里的root指代当前节点,不是根节点】
                # 左右节点都需要传递回这三种情况对应的数量,给当前节点使用
                r_left, p_left, c_left = dfs(root.left) 
                r_right, p_right, c_right = dfs(root.right)
    
                # 当前节点装摄像头【r】:左节点可以是(1)自己装【r_left】(2)靠父节点装【p_left】 (3)靠子节点装【c_left】。右节点同理。
                # 当前节点不装摄像头,其父节点装【p】:左节点可以是(1)自己装【r_left】(2)靠子节点装【c_left】。右节点同理。
                # 当前节点不装摄像头,其子节点装【r】:
                # 1.当靠左节点装【r_left】时,右节点可以是(1)自己装【r_right】(2)靠子节点装【c_right】
                # 2.当靠右节点装【r_right】时,左节点可以是(1)自己装【r_left】(2)靠子节点装【c_left】
                r = min(r_left, p_left, c_left) + min(r_right, p_right, c_right) + 1
                p = min(r_left, c_left) + min(r_right, c_right) 
                c = min(r_left + min(r_right, c_right), r_right + min(r_left, c_left))
    
                return r, p ,c
            
            # 因为根节点没有父节点,所以返回的父节点情况不能用,
            # 但也是可以返回值的,所以可以当作常规节点一起讨论,不用特殊处理
            r, _, c = dfs(root) 
            return min(r, c)
    
    
    ### 解释为什么初始化(当前节点为空)的时候,自己装的情况要返回无穷大 ###
    # 计算c = min(r_left + min(r_right, c_right), r_right + min(r_left, c_left))的时候
    # 左右节点为空,r_left = r_right = inf 时,r = 0, p = 0, c = inf,表示不存在用装子节点的情况(因为我们取min的时候不会取到c)
    # 如果装的情况也要返回0,那么c=0,就会取最小值就会取到c,但是子节点不存在,所以不能这样
    
    

标签:right,root,self,担保,二叉树,III,节点,DP,left
From: https://blog.csdn.net/xying_chloe/article/details/144828559

相关文章

  • 状态机DP学习笔记
    参考:买卖股票的最佳时机【基础算法精讲21】_哔哩哔哩_bilibili ps:笔记中的代码按本人理解整理,重思路,非原视频中的代码,也并非最优代码题目1:买卖股票的最佳时机II(不限交易次数)122.买卖股票的最佳时机II-力扣(LeetCode)思路:第n天结束时的利润= 第n-1天结束时的利润+......
  • DP协议:PHY层
    引言DisplayPort物理层规定了上游设备(例如DisplayPort源或分支设备的AV输出端口)和下游设备(例如DisplayPort接收器或分支设备的AV输入端口)之间直接连接的物理属性。它将数据传输的电气规范从DisplayPort链路层解耦,从而允许链路层具体设计增强的模块化,并且也允许未来传输媒......
  • DAY180内网渗透之内网对抗:横向移动篇&WinRS命令&WinRM管理&RDP终端&密码喷射点&CrackM
    1.内网横向移动1、横向移动篇-协议服务-WinRS&WinRM&RDP2、横向移动篇-工具项目-密码喷射1.1内网横向移动方法分类基于口分ipcsmbwmidcomwinrswinrmrdp等基于漏洞域控提取漏洞Exchange漏洞攻防基于配置委派dysncasrepkerberos攻击ntlmreply1.2WinRM&W......
  • 【Java并发编程线程池】 ForkJoinPool 线程池是什么 怎么工作的 和传统的ThreadPoolEx
    Java中的ForkJoinPool线程池是什么怎么工作的Java中的ForkJoinPool线程池是什么怎么工作的相比较于传统的线程池,ForkJoinPool线程池更适合处理大量的计算密集型任务,它的核心思想是将一个大任务拆分成多个小任务,然后将这些小任务分配给多个线程去执行,最后将这些小任务的......
  • 1367. 二叉树中的链表
    给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。一直向下的路径的意思是:从树中某个节点开始,一直连续向......
  • DP(二)
    byd谁想做课件啊,byd我还有一堆东西没学,byd难过了。读者记得提醒笔者这里面应当含有dp套dp和耳分解内容。本文源码中含有一些<spantitle="">,读者如果感兴趣可以自行找出受影响文字的位置。谁想接DP(一)讲啊/ng,等我把找的题整完了再继续树形DP吧。在开始之前,先来做一......
  • 网络原理(六): UDP 协议
    目录1.UDP协议1.1协议特点1.2协议报文格式1.2.1UDP长度 1.2.2校验和1.UDP协议在进行网络编程时,我们已经对UDP协议进行了简单了解.并且应用层的很多操作,需要调用传输层的提供的接口,基于socketapi来进行完成的.1.1协议特点UDP协议具有以下特......
  • Azure Machine Learning Online Endpoint 使用指南
    AzureMachineLearningOnlineEndpoint使用指南老铁们,今天给大家带来的是关于AzureMachineLearning的神器——OnlineEndpoint。AzureML是一个强大的平台,专门用来构建、训练和部署机器学习模型。它提供了一个模型目录,里面有很多基础和通用模型可供选择。不过,想要......
  • WPF SoundPlayer
    //xaml<Windowx:Class="WpfApp121.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......
  • DP优化——树上依赖性背包&P6326 Shopping
    P6326Shopping题意等价于要买一个连通块。首先如果我们能求出一个dp数组:\(f_{i,j}\)表示\(i\)子树内,有\(j\)元,一定要选\(i\),能得到的最大价值。那么\(f_{1,m}\)就是一定选根的答案。然后点分治即可。接下来就是怎么在合理的复杂度内求出dp数组。直接背包显然......