寒假集训笔记·以点为对象的树形DP

以点为对象的树形DP寒假集训笔记

课件

一、核心概念

1. 定义

以顶点为决策对象的树形DP,核心是通过节点状态关联子树最优解,状态通常定义为 f ( u , s t a t e ) f(u, state) f(u,state):

  • u u u:当前处理的子树根节点
  • s t a t e state state:节点状态(如选/不选、覆盖来源、染色等)
  • 含义:以 u u u 为根的子树中, u u u 处于该状态时的最优答案(最大权值/最小代价)

2. 核心逻辑

决策核心围绕"当前节点状态与直接子节点状态的依赖关系",通过DFS递归遍历树结构,利用子树DP结果推导当前节点DP值,时间复杂度通常为 O ( n ) O(n) O(n)。

二、经典题型详解

(一)最大独立集

例题

1. 问题描述

树上选若干顶点,相邻顶点不能同时选中,求选中顶点的最大权值和。

2. 状态定义
  • d p u 0 dpu0 dpu0:不选节点 u u u 时,以 u u u 为根的子树最大权值
  • d p u 1 dpu1 dpu1:选中节点 u u u 时,以 u u u 为根的子树最大权值
3. 转移方程
  • 选中 u u u:子节点必须不选
    d p u 1 = a u + ∑ v ∈ s o n ( u ) d p v 0 dpu1 = au + \sum_{v \in son(u)} dpv0 dpu1=au+∑v∈son(u)dpv0( a u au au 为节点 u u u 权值)
  • 不选 u u u:子节点可选可不选,取最大值
    d p u 0 = ∑ v ∈ s o n ( u ) m a x ( d p v 0 , d p v 1 ) dpu0 = \sum_{v \in son(u)} max(dpv0, dpv1) dpu0=∑v∈son(u)max(dpv0,dpv1)

( 二 )最小支配集

例题

一、问题描述

每个顶点可以放置守卫,守卫能保护自身及相邻的节点。求最少需要选择多少个顶点放置守卫,使得树上所有节点都被保护(无任何节点遗漏覆盖)。

二、核心分析
  1. 与最大独立集的区别

    最大独立集仅需考虑"节点选或不选"的二元状态,而最小支配集需要明确"节点被谁保护",状态更复杂,需通过多状态区分覆盖来源,避免遗漏保护场景。

  2. 关键思考

    节点的保护来源仅三种可能:自身为守卫、被子节点保护、被父节点保护,因此需设计三状态覆盖所有场景,确保无后效性。

三、状态定义(核心)

定义 d p u s t a t e dpustate dpustate 表示以 u u u 为根的子树中, u u u 处于对应状态时的最少守卫数:

  • d p u 0 dpu0 dpu0: u u u 被选中(自身放置守卫),可覆盖 u u u 及其所有子节点;
  • d p u 1 dpu1 dpu1: u u u 未被选中,但被子节点保护( u u u 无守卫,至少有一个子节点放置了守卫);
  • d p u 2 dpu2 dpu2: u u u 未被选中,依赖父节点保护( u u u 无守卫,需等待父节点的守卫覆盖自身)。
四、状态转移方程

1. d p u 0 dpu0 dpu0 转移( u u u 自身为守卫)

当 u u u 放置守卫时,其所有子节点 v v v 可处于任意状态(无需额外约束,因 u u u 的守卫已覆盖 v v v),只需取每个子节点所有状态的最小值累加,再加上当前节点的守卫数:

dp\[u\]\[0\] = 1 + \\sum_{v \\in children(u)} \\min(dp\[v\]\[0\], dp\[v\]\[1\], dp\[v\]\[2\])

2. d p u 2 dpu2 dpu2 转移( u u u 依赖父节点保护)

当 u u u 未放置守卫且依赖父节点时, u u u 的子节点 v v v 不能依赖 u u u 保护(否则 v v v 会无覆盖),因此 v v v 只能是"自身选中"或"被子节点保护",取两者最小值累加:

dp\[u\]\[2\] = \\sum_{v \\in children(u)} \\min(dp\[v\]\[0\], dp\[v\]\[1\])

3. d p u 1 dpu1 dpu1 转移( u u u 被子节点保护,核心难点)

当 u u u 未放置守卫且需被子节点保护时,必须保证至少有一个子节点 v v v 放置了守卫(即 v v v 处于 d p v 0 dpv0 dpv0 状态) ,否则 u u u 会未被覆盖。转移分两步:

  • Step 1:计算所有子节点取 m i n ( d p v 0 , d p v 1 ) min(dpv0, dpv1) min(dpv0,dpv1) 的总和 S u m Sum Sum,同时记录是否存在子节点满足 d p v 0 ≤ d p v 1 dpv0 ≤ dpv1 dpv0≤dpv1(即该子节点选择 d p v 0 dpv0 dpv0 更优):

    Sum = \\sum_{v \\in children(u)} \\min(dp\[v\]\[0\], dp\[v\]\[1\])

  • Step 2:分情况讨论:

    • Case A:若存在至少一个子节点 v v v 满足 d p v 0 ≤ d p v 1 dpv0 ≤ dpv1 dpv0≤dpv1,则 S u m Sum Sum 中已包含 d p v 0 dpv0 dpv0, u u u 可被该子节点保护,此时 d p u 1 = S u m dpu1 = Sum dpu1=Sum;
    • Case B:若所有子节点均满足 d p v 1 < d p v 0 dpv1 < dpv0 dpv1<dpv0,则 S u m Sum Sum 中全为 d p v 1 dpv1 dpv1(无子女守卫),需强制将一个子节点 v v v 转换为 d p v 0 dpv0 dpv0 状态,取转换成本最小的( d p v 0 − d p v 1 dpv0 - dpv1 dpv0−dpv1)累加至 S u m Sum Sum:

    dp\[u\]\[1\] = Sum + \\min_{v \\in children(u)} { dp\[v\]\[0\] - dp\[v\]\[1\] }

( 三 )一题多解

例题

1. 贪心核心逻辑

每个节点必须被保护(要么自身为守卫,要么被相邻节点保护)。对于深度最深的节点,其保护来源仅两种可能:自身或父节点。贪心选择父节点放置守卫,原因是父节点的覆盖范围更广(可同时保护父节点自身、当前节点及当前节点的兄弟节点),能最小化总守卫数。

2. 贪心执行步骤
  1. 从叶子节点向上遍历树(按深度从大到小排序);
  2. 若当前节点未被覆盖,在其父节点放置守卫,并标记父节点、当前节点及父节点的所有子节点(当前节点的兄弟节点)为已覆盖;
  3. 若遍历至根节点且根节点未被覆盖,则在根节点自身放置守卫。
3. 贪心算法的局限性

仅适用于 所有节点代价相同 的场景(如"放置守卫的成本均为1"),无法处理节点带权的情况(如不同节点放置守卫的成本不同,例如保安站岗)。

二、变式:覆盖半径为2的支配集(消防局的设立

1. 问题描述

与基础最小支配集的核心区别是"守卫覆盖半径为 2 2 2 ":若在节点 C C C 放置守卫,可覆盖C及其距离 ≤ 2 ≤2 ≤2 的所有节点(如边 A − B − C − D − E A-B-C-D-E A−B−C−D−E 中, C C C 的守卫可覆盖 A 、 B 、 C 、 D 、 E A、B、C、D、E A、B、C、D、E 五个节点)。要求找到最少的守卫数,使树上所有节点被覆盖。

2. 核心思考

覆盖半径扩大至 2 2 2 后,节点的覆盖来源更复杂,需考虑"消防局"可能的位置:自身、儿子、孙子、父亲、爷爷。因此需定义多状态,覆盖所有"覆盖来源"和"未覆盖待救援"的场景,确保无遗漏。

3. 五状态定义(核心)

定义 d p u s t a t e dpustate dpustate 表示以 u u u 为根的子树中,对应状态下的最少守卫数,状态需涵盖所有覆盖与未覆盖场景:

  • d p u 0 dpu0 dpu0:至少 u u u 及其子树全部被覆盖,且 u u u 处设消防局(覆盖能力最强,可覆盖 u u u 的爷爷、父亲、自身、儿子、孙子);
  • d p u 1 dpu1 dpu1:至少 u u u 及其子树全部被覆盖, u u u 被其儿子设的消防局覆盖(儿子的消防局半径2,可覆盖 u u u);
  • d p u 2 dpu2 dpu2:至少 u u u 及其子树全部被覆盖, u u u 被其孙子设的消防局覆盖(孙子的消防局半径2,可覆盖 u u u);
  • d p u 3 dpu3 dpu3:至少 u u u 的子树内部全部覆盖,但 u u u 未被覆盖(需等待父亲或爷爷的消防局救援,父亲的消防局半径2可覆盖 u u u,爷爷的消防局半径2也可覆盖 u u u);
  • d p u 4 dpu4 dpu4:至少 u u u 的孙子辈全部覆盖,但 u u u 和儿子都未被覆盖(需等待父节点的消防局救援,父节点的消防局半径2可覆盖 u u u 和 u u u 的儿子)。

4. 状态转移思路(对所有儿子 v v v 操作)

状态 含义 转移逻辑
d p u 0 dpu0 dpu0 u u u 设消防局,自身及子树全覆盖 儿子 v v v 可处于任意状态(因 u u u 的消防局已覆盖 v v v),累加每个儿子所有状态的最小值: ∑ m i n ( d p v 0 , d p v 1 , d p v 2 , d p v 3 , d p v 4 ) \sum min(dpv0, dpv1, dpv2, dpv3, dpv4) ∑min(dpv0,dpv1,dpv2,dpv3,dpv4)
d p u 1 dpu1 dpu1 u u u被子节点消防局覆盖,自身及子树全覆盖 至少有一个儿子 v v v 处于 d p v 0 dpv0 dpv0 状态(儿子设消防局,半径2覆盖 u u u),其余儿子可处于 d p v 0 d p v 3 dpv0~dpv3 dpv0 dpv3 状态(确保子树覆盖且不影响 u u u 的覆盖),取满足条件的最小和
d p u 2 dpu2 dpu2 u u u 被孙子消防局覆盖,自身及子树全覆盖 至少有一个儿子 v v v 处于 d p v 1 dpv1 dpv1 状态(孙子设消防局,通过儿子传递覆盖 u u u),其余儿子可处于 d p v 0 d p v 2 dpv0~dpv2 dpv0 dpv2 状态,取满足条件的最小和
d p u 3 dpu3 dpu3 u u u 子树全覆盖, u u u 未覆盖(等父/爷爷救援) 所有儿子 v v v 必须已被覆盖(不能依赖 u u u 救援),因此儿子仅能处于 d p v 0 d p v 2 dpv0~dpv2 dpv0 dpv2 状态,累加每个儿子的最小值: ∑ m i n ( d p v 0 , d p v 1 , d p v 2 ) \sum min(dpv0, dpv1, dpv2) ∑min(dpv0,dpv1,dpv2)
d p u 4 dpu4 dpu4 孙子辈全覆盖, u u u 和儿子未覆盖(等父节点救援) 所有儿子 v v v 的孙子辈已覆盖,儿子可处于 d p v 0 d p v 3 dpv0~dpv3 dpv0 dpv3 状态(儿子未覆盖可由父节点 u u u 的父节点救援),累加每个儿子的最小值: ∑ m i n ( d p v 0 , d p v 1 , d p v 2 , d p v 3 ) \sum min(dpv0, dpv1, dpv2, dpv3) ∑min(dpv0,dpv1,dpv2,dpv3)

5. 实现技巧

d p u 1 dpu1 dpu1 和 d p u 2 dpu2 dpu2 的转移需确保"至少一个儿子满足特定状态",可利用 d p u 4 dpu4 dpu4 和 d p u 3 dpu3 dpu3 简化计算:

  • d p u 1 dpu1 dpu1:先计算 d p u 4 dpu4 dpu4(儿子取 m i n ( d p v 0 d p v 3 ) min(dpv0~dpv3) min(dpv0 dpv3) 的总和),再扫描所有儿子,找到"替换为 d p v 0 dpv0 dpv0 成本最小"的情况,即:

d p u 1 = m i n ( d p u 1 , d p u 4 − m i n ( d p v 0 d p v 3 ) + d p v 0 ) dpu1 = min(dpu1, dpu4 - min(dpv0~dpv3) + dpv0) dpu1=min(dpu1,dpu4−min(dpv0 dpv3)+dpv0)

  • d p u 2 dpu2 dpu2:先计算 d p u 3 dpu3 dpu3(儿子取 m i n ( d p v 0 d p v 2 ) min(dpv0~dpv2) min(dpv0 dpv2) 的总和),再扫描所有儿子,找到"替换为 d p v 1 dpv1 dpv1 成本最小"的情况,即:

d p u 2 = m i n ( d p u 2 , d p u 3 − m i n ( d p v 0 d p v 2 ) + d p v 1 ) dpu2 = min(dpu2, dpu3 - min(dpv0~dpv2) + dpv1) dpu2=min(dpu2,dpu3−min(dpv0 dpv2)+dpv1)

相关推荐
wabs6661 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964131 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
basketball6161 小时前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++
嗝o゚2 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本2 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
Fre丸子_3 小时前
自定义文件夹选取功能
c++
Ulyanov4 小时前
用声明式语法重新定义Python桌面UI:QML+PySide6现代开发入门(一)
开发语言·python·算法·ui·系统仿真·雷达电子对抗仿真
数据科学小丫4 小时前
特征工程处理
人工智能·算法·机器学习
z落落4 小时前
C#参数区别
java·算法·c#
思麟呀5 小时前
C++工业级日志项目(六)异步日志器
linux·c++·windows