小欧吃苹果
[题目链接](https://www.nowcoder.com/practice/1e681787c63045cdbfc26b9b09002aeb)
思路
一棵 个结点的树,结点
上的人当前有
个苹果,想吃恰好
个。每次操作可以沿一条边传递 1 个苹果,求最小总传递次数。
关键观察:边上的流量
对于树上的任意一条边 ,删掉这条边后树被分成两个连通分量。设其中一个分量的结点集合为
,则
中所有人的"盈余"之和为:
$$
如果 ,说明
中有多余的苹果需要向外运出;如果
,说明需要从外面运入。无论哪种情况,经过边
的苹果传递次数恰好是
。
因此,最小总传递次数等于所有边上流量之和:
$$
实现方法
以结点 1 为根,对树做一次 BFS,然后按逆 BFS 序(从叶子到根)累加每个子树的盈余。对于每个非根结点 ,
就是边
上的流量,将它累加到答案中,然后把
加到
上。
样例演示
树的结构:1-2, 1-3, 2-4,苹果分布 。
各结点盈余:,
,
,
。
- 结点 4:
,累加到父结点 2:
- 结点 3:
,累加到父结点 1:
- 结点 2:
,累加到父结点 1:
答案 ,与预期一致。
复杂度
- 时间复杂度:
,一次 BFS 加一次逆序遍历。
- 空间复杂度:
,存储树和辅助数组。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
vector<long long> a(n + 1);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
vector<vector<int>> adj(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
scanf("%d %d", &u, &v);
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<long long> sub(n + 1);
for (int i = 1; i <= n; i++) sub[i] = a[i] - i;
vector<int> parent(n + 1, 0);
vector<bool> vis(n + 1, false);
vector<int> order;
queue<int> q;
q.push(1); vis[1] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
order.push_back(u);
for (int v : adj[u]) {
if (!vis[v]) {
vis[v] = true;
parent[v] = u;
q.push(v);
}
}
}
long long ans = 0;
for (int i = n - 1; i >= 1; i--) {
int u = order[i];
ans += abs(sub[u]);
sub[parent[u]] += sub[u];
}
printf("%lld\n", ans);
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
StringTokenizer st = new StringTokenizer(br.readLine().trim());
long[] a = new long[n + 1];
for (int i = 1; i <= n; i++) a[i] = Long.parseLong(st.nextToken());
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i <= n; i++) adj.add(new ArrayList<>());
for (int i = 0; i < n - 1; i++) {
st = new StringTokenizer(br.readLine().trim());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
adj.get(u).add(v);
adj.get(v).add(u);
}
long[] sub = new long[n + 1];
for (int i = 1; i <= n; i++) sub[i] = a[i] - i;
int[] parent = new int[n + 1];
boolean[] vis = new boolean[n + 1];
int[] order = new int[n];
int head = 0, tail = 0;
order[tail++] = 1;
vis[1] = true;
while (head < tail) {
int u = order[head++];
for (int v : adj.get(u)) {
if (!vis[v]) {
vis[v] = true;
parent[v] = u;
order[tail++] = v;
}
}
}
long ans = 0;
for (int i = n - 1; i >= 1; i--) {
int u = order[i];
ans += Math.abs(sub[u]);
sub[parent[u]] += sub[u];
}
System.out.println(ans);
}
}

京公网安备 11010502036488号