洛谷P1364 医院设置
题目描述
设有一棵二叉树,如图:
其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 11。如上图中,若医院建在1 处,则距离和
=
4
+
12
+
2
×
20
+
2
×
40
=
136
=
4
+
12
+
2
×
20
+
2
×
40
=
136
=4+12+2\times20+2\times40=136=4+12+2×20+2×40=136
=4+12+2×20+2×40=136=4+12+2×20+2×40=136;若医院建在 33 处,则距离和
=
4
×
2
+
13
+
20
+
40
=
81
=
4
×
2
+
13
+
20
+
40
=
81
=4\times2+13+20+40=81=4×2+13+20+40=81
=4×2+13+20+40=81=4×2+13+20+40=81。
输入格式
第一行一个整数 n,表示树的结点数。
接下来的 n 行每行描述了一个结点的状况,包含三个整数 w, u, v,其中 w 为居民人口数,u 为左链接(为 0 表示无链接),v 为右链接(为 0 表示无链接)。
输出格式
一个整数,表示最小距离和。
输入输出样例
输入 #1
5
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0
输出 #1
81
说明/提示
数据规模与约定
对于
100
%
100\%
100%的数据,保证
1
≤
n
≤
100
,
0
≤
u
,
v
≤
n
1 \leq n \leq 100, 0 \leq u, v \leq n
1≤n≤100,0≤u,v≤n ,
0
≤
u
,
v
≤
n
0≤u,v≤n
0≤u,v≤n ,
1
≤
w
≤
1
0
5
1 \leq w \leq 10^5
1≤w≤105 。
可以看到 n ≤ 100 n\leq 100 n≤100,所以不会超时的,哪怕用弗洛伊德这种也没关系。但要时间复杂度最低的话,这个问题还是第一次遇到。题目的大意就是,在这个树上找一个点,使得所有点到这个点的距离之和最小。如果遍历n个点作为医院,也不是不可以,但是时间复杂度就不是最优了。
我们试着看看相邻的两个点作为医院的时候是否存在着某种联系,其实这就是一种树形dp。假设原来是u作为医院,现在用u的子节点v做医院。对于v为根的子树,这是一个好消息,因为这个子树上所有的点距离都-1了。而对于其他所有的点,这是一个坏消息,因为所有点本来到u就可以了,现在还要再走一步到v,距离还要+1。所以dp方程就出来了:
d
p
[
v
]
=
d
p
[
u
]
−
s
i
z
e
[
v
]
+
s
i
z
e
[
r
o
o
t
]
−
s
i
z
e
[
v
]
dp[v]=dp[u]-size[v]+size[root]-size[v]
dp[v]=dp[u]−size[v]+size[root]−size[v]
其中dp[i]表示i作为医院时的总距离,size[i]表示i作为根的子树的大小。那么思路就出来了,预处理一下size数组,然后找到根,从根开始遍历,先算出dp[root],再遍历一次算出dp数组。代码如下:
//
// main.cpp
// size[i]表示i为根的子树大小;dp[i]是i作为根的总距离
// dfs预处理得到size数组
// dfs2预处理得到dp[root]
// dfs3计算dp数组
// Copyright © 2020 ji luyang. All rights reserved.
//
#include <iostream>
#include <limits.h>
#include <cstdio>
#include <cmath>
#include <stack>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
#include <queue>
#include <cstring>
#include <map>
#include <list>
#include <set>
using namespace std;
int n,root;
struct node{
int val,id,l,r;
}nodes[110];
int size[110],dp[110];
int res=INT_MAX;
void dfs(int i){
if(!nodes[i].id){
size[i]=0;
return;
}
if(nodes[i].l==0&&nodes[i].r==0){
size[i]=nodes[i].val;
return ;
}
dfs(nodes[i].l);
dfs(nodes[i].r);
size[i]=size[nodes[i].l]+size[nodes[i].r]+nodes[i].val;
return;
}
void dfs2(int i,int h){
dp[root]+=nodes[i].val*h;
if(nodes[i].l) dfs2(nodes[i].l,h+1);
if(nodes[i].r) dfs2(nodes[i].r,h+1);
}
void dfs3(int i){
if(nodes[i].l){
dp[nodes[i].l]=dp[i]-2*size[nodes[i].l]+size[root];
dfs3(nodes[i].l);
}
if(nodes[i].r){
dp[nodes[i].r]=dp[i]-2*size[nodes[i].r]+size[root];
dfs3(nodes[i].r);
}
}
int main(){
cin>>n;
root=n*(n+1)/2;
memset(nodes,0,sizeof(nodes));
memset(dp,0,sizeof(dp));
memset(size,0,sizeof(size));
for(int i=1;i<=n;i++){
scanf("%d %d %d",&nodes[i].val,&nodes[i].l,&nodes[i].r);
if(nodes[i].l) root-=nodes[i].l;
if(nodes[i].r) root-=nodes[i].r;
nodes[i].id=i;
}
dfs(root);
dfs2(root,0);
dfs3(root);
for(int i=1;i<=n;i++) res=min(res,dp[i]);
cout<<res;
return 0;
}