P1364 医院设置(树的重心)

题目描述

设有一棵二叉树,如图:
在这里插入图片描述
其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 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 1n1000u,vn1w105

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);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51864047

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值