1 条题解

  • 0
    @ 2025-10-22 17:45:32

    问题分析

    我们有一棵 nn 个节点的树,需要在某些节点放置灭火器。每个灭火器:

    • 最多覆盖 ss 个节点(包括自身)
    • 覆盖半径最多为 kk(即距离不超过 kk 条边)

    要求找出覆盖所有节点的最少灭火器数量。

    关键观察

    1. 覆盖半径的重要性

    由于 k20k \leq 20,这是一个重要的约束条件。我们可以设计基于距离的状态,而不会导致状态爆炸。

    2. 贪心策略

    直觉上,我们应该从叶子节点向根节点处理,在需要时放置灭火器。这样可以最大化每个灭火器的覆盖范围。

    3. 状态设计思路

    对于每个节点,我们需要知道:

    • 该节点子树中还有多少节点未被覆盖
    • 这些未覆盖节点距离当前节点的距离分布

    解法思路

    树形DP + 贪心

    状态定义

    dp[u][d]dp[u][d] 表示在节点 uu 的子树中,距离 uu 恰好为 dd未被覆盖的节点数量

    这里 dd 的范围是 00kk,因为:

    • 距离超过 kk 的节点无法被 uu 处的灭火器覆盖
    • 我们需要记录这些信息来让祖先节点处理

    状态转移

    从子节点到父节点: 对于节点 uu 和其子节点 vv,子节点中距离 vvdd 的未覆盖节点,在父节点 uu 看来距离为 d+1d+1

    dp[u][d+1]+=dp[v][d]dp[u][d+1] += dp[v][d]

    在节点放置灭火器: 当我们在节点 uu 放置灭火器时,它可以覆盖距离不超过 kk 的所有节点。因此:

    • 清空 dp[u][0]dp[u][0]dp[u][k]dp[u][k] 中尽可能多的未覆盖节点
    • 但总数不能超过 ss

    贪心决策时机

    我们采用自底向上的DFS遍历:

    1. 处理子树:先递归处理所有子节点,收集未覆盖节点信息
    2. 检查当前节点:如果 dp[u][k]>0dp[u][k] > 0,说明有节点距离当前节点正好 kk,这些节点必须被覆盖(否则它们无法被任何祖先覆盖)
    3. 放置灭火器:在需要时在当前节点放置灭火器,尽量覆盖更多的未覆盖节点
    4. 向父节点汇报:将剩余的未覆盖节点信息传递给父节点

    特殊处理根节点

    遍历完成后,根节点可能还有未覆盖的节点,需要在根节点处放置额外的灭火器来覆盖它们。

    算法步骤

    1. 初始化:构建树的邻接表,初始化DP数组
    2. DFS遍历:从叶子节点开始,自底向上处理:
      • 合并子节点的未覆盖信息
      • 检查是否需要放置灭火器
      • 更新灭火器计数
      • 向父节点传递剩余未覆盖信息
    3. 处理根节点:根节点处剩余的未覆盖节点需要额外的灭火器
    4. 输出结果:总灭火器数量

    复杂度分析

    • 时间复杂度O(nk)O(n \cdot k)
      • 每个节点处理一次,每次处理 O(k)O(k) 的状态
      • 由于 k20k \leq 20,实际运行效率很高
    • 空间复杂度O(nk)O(n \cdot k)
      • 存储每个节点的距离分布信息

    关键难点

    1. 状态设计:如何用有限的状态表示子树的覆盖情况
    2. 贪心策略:在哪个节点放置灭火器能最大化覆盖效果
    3. 边界处理:距离正好为 kk 的节点必须被当前覆盖

    优化技巧

    • 尽早覆盖:当发现距离为 kk 的未覆盖节点时,立即在当前节点放置灭火器
    • 容量最大化:每个灭火器尽量覆盖 ss 个节点,避免浪费
    • 信息传递:只传递必要的未覆盖信息给祖先节点

    总结

    本题是一个典型的树上覆盖问题,通过巧妙的树形DP状态设计和贪心策略,可以在 O(nk)O(nk) 的时间内解决。核心思想是自底向上处理,在距离限制即将失效时放置灭火器,从而用最少的资源完成全覆盖任务。

    • 1

    信息

    ID
    3753
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    7
    已通过
    1
    上传者