1 条题解
-
0
问题分析
我们需要在树上放置最少数量的灭火器,使得:
- 每个节点都能被距离不超过 的灭火器覆盖
- 每个灭火器最多覆盖 个节点
这是一个树上的覆盖问题,需要最小化灭火器数量。
算法思路
核心思想:树形动态规划 + 贪心匹配
使用自底向上的树形DP,维护每个节点的覆盖需求和可用资源,通过贪心策略在子树内尽可能匹配需求和资源。
状态定义
f[x][j]:在节点 的子树中,需要被距离 恰好为 的灭火器覆盖的节点数量r[x][j]:在节点 的子树中,距离 恰好为 的灭火器还能覆盖的节点数量
关键观察
- 距离传递性:子节点中距离子节点为 的需求,对应父节点中距离为 的需求
- 资源继承性:子节点中距离子节点为 的资源,对应父节点中距离为 的资源
- 贪心匹配:优先用距离较远的资源覆盖较远的需求(避免浪费覆盖范围)
代码详解
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. 放置新灭火器
当存在距离 的需求时,必须在当前节点放置灭火器:
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);算法正确性
状态转移的正确性
- 需求传递:子节点中距离 的需求,在父节点看来确实是距离 的需求
- 资源传递:子节点中距离 的资源,可以覆盖父节点中距离 的需求
- 覆盖完整性:通过自底向上的处理,确保所有需求都被考虑
贪心策略的最优性
- 远距离优先:远距离资源覆盖远距离需求,避免资源浪费
- 局部最优:在子树内尽可能匹配,减少向上传递的需求
复杂度分析
时间复杂度
- DFS遍历:
- 状态转移:每个节点 (最坏情况)
- 总复杂度:,在 时可行
空间复杂度
- :存储
f和r数组 - :存储树的邻接表
样例分析
对于样例:
n=10, s=10, k=3- 所有节点可以被1个灭火器覆盖(因为 )
- 输出结果为1
算法优势
- 高效性:利用树形结构避免重复计算
- 最优性:贪心策略确保灭火器数量最小
- 通用性:适用于各种树结构和参数设置
- 实现简洁:核心算法逻辑清晰
总结
本题通过巧妙的树形DP设计和贪心匹配策略,高效解决了树上的最小覆盖问题。算法的核心创新在于:
- 使用距离维度的状态表示
- 自底向上的需求-资源匹配
- 贪心的远距离优先匹配策略
该解法在时间复杂度和最优性之间取得了良好平衡,体现了树形动态规划与贪心算法的完美结合。
- 1
信息
- ID
- 4993
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者