1 条题解

  • 0
    @ 2025-11-4 23:18:05

    问题分析

    我们需要在树上放置最少数量的灭火器,使得:

    • 每个节点都能被距离不超过 kk 的灭火器覆盖
    • 每个灭火器最多覆盖 ss 个节点

    这是一个树上的覆盖问题,需要最小化灭火器数量。

    算法思路

    核心思想:树形动态规划 + 贪心匹配

    使用自底向上的树形DP,维护每个节点的覆盖需求可用资源,通过贪心策略在子树内尽可能匹配需求和资源。

    状态定义

    • f[x][j]:在节点 xx 的子树中,需要被距离 xx 恰好为 jj 的灭火器覆盖的节点数量
    • r[x][j]:在节点 xx 的子树中,距离 xx 恰好为 jj 的灭火器还能覆盖的节点数量

    关键观察

    1. 距离传递性:子节点中距离子节点为 j1j-1 的需求,对应父节点中距离为 jj 的需求
    2. 资源继承性:子节点中距离子节点为 jj 的资源,对应父节点中距离为 j1j-1 的资源
    3. 贪心匹配:优先用距离较远的资源覆盖较远的需求(避免浪费覆盖范围)

    代码详解

    1. 初始化与DFS框架

    void dfs(int x, int fa) {
        f[x][0] = 1;  // 当前节点自身需要覆盖(距离0)
        
        for (int i = 0; i < g[x].size(); i++) {
            int kk = g[x][i];
            if (fa == kk) continue;
            
            dfs(kk, x);  // 递归处理子树
            
            // 合并子树信息
            for (int j = 1; j <= k; j++) {
                f[x][j] += f[kk][j - 1];  // 子需求 → 父需求(距离+1)
                r[x][j - 1] += r[kk][j];  // 子资源 → 父资源(距离-1)
            }
        }
        // ... 后续处理
    }
    

    2. 放置新灭火器

    当存在距离 kk 的需求时,必须在当前节点放置灭火器:

    if (f[x][k]) {
        int t = f[x][k] / s + (f[x][k] % s != 0);  // 计算所需灭火器数量
        ans += t;
        r[x][k] += t * s;  // 记录新增灭火器的覆盖能力
    }
    

    3. 贪心匹配需求与资源

    int p = k;  // 从最远距离开始寻找可用资源
    for (int i = k; i >= 0; i--) {  // 从最远需求开始处理
        while (f[x][i]) {  // 处理所有距离i的需求
            // 找到距离≥i的可用资源
            while (!r[x][p] && p >= i) p--;
            if (p < i) break;  // 无可用资源
            
            // 消耗资源覆盖需求
            int t = min(r[x][p], f[x][i]);
            r[x][p] -= t;
            f[x][i] -= t;
        }
    }
    

    贪心策略原理:优先用较远的资源覆盖较远的需求,保留较近的资源用于覆盖更难满足的近处需求。

    4. 根节点特殊处理

    // 处理根节点未被覆盖的剩余需求
    ll cnt = 0;
    for (int i = 0; i <= k; i++)
        cnt += f[1][i];
    ans += cnt / s + (cnt % s != 0);
    

    算法正确性

    状态转移的正确性

    1. 需求传递:子节点中距离 j1j-1 的需求,在父节点看来确实是距离 jj 的需求
    2. 资源传递:子节点中距离 jj 的资源,可以覆盖父节点中距离 j1j-1 的需求
    3. 覆盖完整性:通过自底向上的处理,确保所有需求都被考虑

    贪心策略的最优性

    • 远距离优先:远距离资源覆盖远距离需求,避免资源浪费
    • 局部最优:在子树内尽可能匹配,减少向上传递的需求

    复杂度分析

    时间复杂度

    • DFS遍历O(n)O(n)
    • 状态转移:每个节点 O(k2)O(k^2)(最坏情况)
    • 总复杂度O(nk2)O(nk^2),在 n105,k20n \leq 10^5, k \leq 20 时可行

    空间复杂度

    • O(nk)O(nk):存储 fr 数组
    • O(n)O(n):存储树的邻接表

    样例分析

    对于样例:

    n=10, s=10, k=3
    
    • 所有节点可以被1个灭火器覆盖(因为 s=10ns=10 \geq n
    • 输出结果为1

    算法优势

    1. 高效性:利用树形结构避免重复计算
    2. 最优性:贪心策略确保灭火器数量最小
    3. 通用性:适用于各种树结构和参数设置
    4. 实现简洁:核心算法逻辑清晰

    总结

    本题通过巧妙的树形DP设计和贪心匹配策略,高效解决了树上的最小覆盖问题。算法的核心创新在于:

    • 使用距离维度的状态表示
    • 自底向上的需求-资源匹配
    • 贪心的远距离优先匹配策略

    该解法在时间复杂度和最优性之间取得了良好平衡,体现了树形动态规划与贪心算法的完美结合。

    • 1

    信息

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