1 条题解
-
0
这是一道树形DP与二分答案结合的题目,难度较高。下面给出详细题解。
题目理解
Bajtazar需要在一棵个节点的树(城市路网)上,从根节点(披萨店)出发,访问所有个其他节点(客户),最多进行次配送,求最短的加热器总运行时间。
关键点:
- 每次配送:从披萨店出发 → 访问若干客户 → 返回披萨店
- 加热器只在配送途中开启,停留时间不计
- 目标是最小化加热器总运行时间(即所有配送路径长度之和)
问题转化
将问题抽象为:在树上找至多条从根出发的路径,覆盖所有节点,使得路径总长度最小。
观察:如果允许,那么每条路径只访问一个客户,总时间固定。当时,需要在单次配送中访问多个客户,精心规划路线。
核心算法思路
1. 二分答案 + 树形DP验证
二分答案框架:
- 答案具有单调性:加热时间越长,需要的配送次数越少
- 二分加热时间,判断能否用次配送完成
判定问题:能否用条从根出发的路径,每条长度,覆盖整棵树?
2. 树形DP状态设计
对于子树,定义状态:
- :覆盖子树所需的最少路径数
- 或更精细的状态:表示用条路径覆盖子树的最小总时间
但由于较大,需要更高效的设计。
3. 贪心策略
实际求解时常用贪心:
- 预处理所有节点到根的距离
- 按从大到小排序(最远的先处理)
- 每次选择未覆盖且最远的点,找能覆盖它的最长路径
标准解法详解
步骤1:预处理
- 建树,计算每个节点到根的距离
- 求出树的直径等信息
步骤2:关键性质
性质1:每条配送路径的时间 = 该路径上到根最远点距离 × 2 - 重复利用的边权
性质2:最优解中,每条路径必然结束于某个叶子节点(或延伸至最远点)
步骤3:算法实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; vector<pair<int, int>> g[N]; ll dist[N]; int n, k; void dfs(int u, int p) { for (auto [v, w] : g[u]) { if (v == p) continue; dist[v] = dist[u] + w; dfs(v, u); } } bool check(ll T) { // 判断能否在T总时间内用≤k次配送完成 // 实现细节:贪心选择路径 int cnt = 0; vector<bool> covered(n + 1, false); // 按距离排序,最远的先处理 vector<pair<ll, int>> nodes; for (int i = 2; i <= n; i++) { nodes.emplace_back(dist[i], i); } sort(nodes.rbegin(), nodes.rend()); for (auto [d, u] : nodes) { if (covered[u]) continue; // 找到从根到u的路径上最远的未覆盖点 ll max_dist = d; int v = u; while (v != 1 && !covered[v]) { max_dist = max(max_dist, dist[v]); covered[v] = true; // 找父节点... } if (max_dist * 2 > T) return false; cnt++; if (cnt > k) return false; } return cnt <= k; } int main() { cin >> n >> k; for (int i = 1; i < n; i++) { int a, b, c; cin >> a >> b >> c; g[a].emplace_back(b, c); g[b].emplace_back(a, c); } dist[1] = 0; dfs(1, 0); // 二分答案 ll left = 0, right = 2e14, ans = 0; while (left <= right) { ll mid = (left + right) / 2; if (check(mid)) { ans = mid; right = mid - 1; } else { left = mid + 1; } } cout << ans << endl; return 0; }
样例分析
对于样例:
7 3 1 2 5 2 3 11 2 4 2 5 2 6 1 6 1 7 1 1
最优配送方案:
- $1 \rightarrow 2 \rightarrow 4 \rightarrow 2 \rightarrow 5 \rightsquigarrow 1$(15分钟)
- (16分钟)
- $1 \rightarrow 6 \rightarrow 1 \rightarrow 7 \rightsquigarrow 1$(3分钟)
总时间:15 + 16 + 3 = 34分钟
复杂度分析
- 预处理:DFS
- 二分答案:
- check函数:(排序)
- 总复杂度:,其中是答案范围
算法标签
- 树形DP / 树形贪心
- 二分答案
- 路径覆盖
- 最近公共祖先(LCA)
总结
这道题是树结构上的路径覆盖问题,通过二分答案将最优化问题转化为判定问题,再运用树形贪心策略进行验证。关键在于发现路径覆盖的性质并设计高效的检查算法。
对于的大数据范围,的算法能够通过所有测试点。
- 1
信息
- ID
- 3430
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者