树的性质:利用树的唯一路径特性,避免复杂的最短路径计算。
定理:一定先完成同一子树下目标点的遍历,故需要记录父节点和遍历次序,通过逆序bfs来实现自下而上,通过父节点记录来累加得到父节点的累计值(因为记录了父节点故有严格的两边关系,不需要按照dfs的后序来实现子树下的累加)
- 分步骤拆解
建树:用邻接表存储树结构。
BFS预处理:计算父节点和拓扑序,为子树统计做准备。
统计任务点:初始化每个节点的cntS(取货点)和cntT(送货点)。
子树汇总:逆序处理拓扑序,自底向上累加子树的任务点数量。
计算总路程:遍历所有边,根据cntS和cntT判断是否经过,累加边权。
例子:6.11华为.1 https://niumacode.com/training/131/problem/P1687
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
// 输入处理
int N, M;
cin >> N >> M;
vector<vector<pair<int, int>>> adj(N + 1);
for (int i = 0; i < N - 1; ++i) {
int u, v, c;
cin >> u >> v >> c;
adj[u].push_back({v, c});
adj[v].push_back({u, c});
}
// BFS建立父节点和拓扑序
vector<int> fa(N + 1, 0), order;
queue<int> q;
q.push(1);
fa[1] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
order.push_back(u);
for (auto [v, c] : adj[u]) {
if (v != fa[u]) {
fa[v] = u;
q.push(v);
}
}
}
// 初始化任务点统计
vector<int> cntS(N + 1, 0), cntT(N + 1, 0);
for (int i = 0; i < M; ++i) {
int s, t;
cin >> s >> t;
cntS[s]++;
cntT[t]++;
}
// 逆序汇总子树
for (int i = order.size() - 1; i >= 0; --i) {
int u = order[i];
if (fa[u] != 0) {
cntS[fa[u]] += cntS[u];
cntT[fa[u]] += cntT[u];
}
}
// 计算总路程
long long sumS = 0, sumT = 0;
for (int u = 2; u <= N; ++u) {
int p = fa[u];
for (auto [v, c] : adj[u]) {
if (v == p) {
if (cntS[u] > 0) sumS += c;
if (cntT[u] > 0) sumT += c;
break;
}
}
}
cout << 2 * (sumS + sumT) << endl;
return 0;
}
第二题同样是树结构,但是主要和深度、叶子节点有关,故需要储存的是children数组
-
输入处理:读取设备数量n和连接关系,构建邻接表adj。
-
DFS遍历:计算每个节点的深度depth和子节点列表children,同时记录最大深度max_depth。
-
动态规划求解:
对每个可能的深度h(从0到max_depth):按深度分层存储节点by_depth。
初始化best数组,best[v]表示以v为根的子树在目标深度h时的最优解。
从最大深度开始反向计算:如果节点深度大于h,设为极小值。
如果节点深度等于h,设为1(保留该节点)。
否则,累加子节点的有效解,并更新当前节点的best值。
记录根节点的最优解answer。
输出结果:需要移除的设备数为n - answer。#include <iostream> #include <vector> #include <algorithm> using namespace std; vector<vector<int>> adj; vector<int> depth; vector<vector<int>> children; int max_depth = 0; void dfs(int u, int p) { for (int v : adj[u]) { if (v == p) continue; depth[v] = depth[u] + 1; max_depth = max(max_depth, depth[v]); children[u].push_back(v); dfs(v, u); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; adj.resize(n + 1); depth.resize(n + 1); children.resize(n + 1); for (int i = 0; i < n - 1; ++i) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } dfs(1, 0); int answer = 0; for (int h = 0; h <= max_depth; ++h) { vector<vector<int>> by_depth(max_depth + 1); for (int v = 1; v <= n; ++v) { by_depth[depth[v]].push_back(v); } vector<int> best(n + 1, -1e9); for (int d = max_depth; d >= 0; --d) { for (int v : by_depth[d]) { if (depth[v] > h) { best[v] = -1e9; } else if (depth[v] == h) { best[v] = 1; } else { int s = 0; for (int u : children[v]) { if (best[u] > 0) { s += best[u]; } } best[v] = (s > 0) ? (s + 1) : -1e9; } } } answer = max(answer, best[1]); } cout << n - answer << endl; return 0; }
浙公网安备 33010602011771号