树的性质:利用树的唯一路径特性,避免复杂的最短路径计算。
定理:一定先完成同一子树下目标点的遍历,故需要记录父节点和遍历次序,通过逆序bfs来实现自下而上,通过父节点记录来累加得到父节点的累计值(因为记录了父节点故有严格的两边关系,不需要按照dfs的后序来实现子树下的累加)
- 分步骤拆解
建树:用邻接表存储树结构。
BFS预处理:计算父节点和拓扑序,为子树统计做准备。
统计任务点:初始化每个节点的cntS(取货点)和cntT(送货点)。
子树汇总:逆序处理拓扑序,自底向上累加子树的任务点数量。
计算总路程:遍历所有边,根据cntS和cntT判断是否经过,累加边权。
例子:6.11华为.1 https://niumacode.com/training/131/problem/P1687
include
include
include
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
queue
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
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
include
include
using namespace std;
vector<vector
> adj;
vectordepth;
vector<vector> 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> by_depth(max_depth + 1);
for (int v = 1; v <= n; ++v) {
by_depth[depth[v]].push_back(v);
}vector
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号