题目描述
设有一棵二叉树,如图:
其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为
1
1
1。如上图中,若医院建在
1
1
1 处,则距离和
=
4
+
12
+
2
×
20
+
2
×
40
=
136
=4+12+2\times20+2\times40=136
=4+12+2×20+2×40=136;若医院建在
3
3
3 处,则距离和
=
4
×
2
+
13
+
20
+
40
=
81
=4\times2+13+20+40=81
=4×2+13+20+40=81。
输入格式
第一行一个整数 n n n,表示树的结点数。
接下来的 n n n 行每行描述了一个结点的状况,包含三个整数 w , u , v w, u, v w,u,v,其中 w w w 为居民人口数, u u u 为左链接(为 0 0 0 表示无链接), v v v 为右链接(为 0 0 0 表示无链接)。
输出格式
一个整数,表示最小距离和。
输入输出样例
输入
5
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0
输出
81
说明/提示
对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 100 , 0 ≤ u , v ≤ n , 1 ≤ w ≤ 1 0 5 1 \leq n \leq 100,0 \leq u, v \leq n,1 \leq w \leq 10^5 1≤n≤100,0≤u,v≤n,1≤w≤105 。
O(n)做法
开两个数组 S i z e [ N ] 、 f [ N ] Size[N]、f[N] Size[N]、f[N]。 S i z e [ i ] Size[i] Size[i] 表示以 i i i 结点为根的子树大小; f [ i ] f[i] f[i] 表示,所有人到达结点 i i i 的总路径
从任意点开始,不妨从 1 1 1 号结点开始, d f s dfs dfs 一次,预处理出 S i z e [ i ] Size[i] Size[i] 与 f [ 1 ] f[1] f[1] 。
第一次 d f s dfs dfs 我们只计算出了 f [ 1 ] f[1] f[1] 。再进行一次 d f s dfs dfs ,计算出全部的 f [ i ] f[i] f[i] ,更新最值即可得到答案。转移方法为:设已知 f [ x ] f[x] f[x] , y y y 是 x x x 的子节点,那么 f [ y ] = f [ x ] − 2 × S i z e [ y ] + S i z e [ 1 ] f[y]=f[x]-2\times Size[y]+Size[1] f[y]=f[x]−2×Size[y]+Size[1]。
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=10100;
int n,l,r,a[N],vis[N],Size[N],f[N],mn=(1<<30);
int head[N],ver[M],Next[M],tot;
void add(int x,int y){
ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
int dfs(int x){
int ret=0;
vis[x]=1; Size[x]=a[x];
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(vis[y]) continue;
ret+=dfs(y); Size[x]+=Size[y];
}
return ret+Size[x]-a[x];
}
void tran(int x){
vis[x]=2;
mn=min(mn,f[x]);
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(vis[y]==2) continue;
f[y]=f[x]-Size[y]+Size[1]-Size[y];
tran(y);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i],&l,&r);
if(l) add(i,l),add(l,i);
if(r) add(i,r),add(r,i);
}
f[1]=dfs(1);
tran(1);
printf("%d\n",mn);
}