题目描述
给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。
输入格式
第一行包含整数 n。
接下来 n−1 行,每行包含三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,表示点 a i a_i ai 和 b i b_i bi 之间存在一条权值为 c i c_i ci 的边。
输出格式
输出一个整数,表示所求点到树中其他结点的最远距离。
数据范围
1
≤
n
≤
10000
1≤n≤10000
1≤n≤10000,
1
≤
a
i
,
b
i
≤
n
1≤a_i,b_i≤n
1≤ai,bi≤n,
1
≤
c
i
≤
1
0
5
1≤c_i≤10^5
1≤ci≤105
输入样例
5
2 1 1
3 2 1
4 3 1
5 1 1
输出样例
2
算法思想(DFS)
根据题目描述树的中点到树中其他结点的最远距离最近,那么可以先计算出每个点到其它各点的最远距离,然后求其中的最小值。如下图所示:
在边权都为1的情况下,结点2到其它各点的最远距离为2,最小,因此结点2是树的中心。
那么如何求所有结点到其它结点的最远距离呢?以结点 u u u为例,经过它的路径可以分为两类:
- 从结点 u u u向下走的,即经过它子结点的路径。可以通过DFS求出其中的最远距离。
- 从结点
u
u
u向上走的,即经过它父结点的路径。此时情况比较复杂,不妨设父结点的编号为
v
v
v,那么求经过
v
v
v的最长路径又可以分为两类:
- 结点 v v v继续向上走的最长路径
- 结点 v v v向下经过其它子结点的最长路径。注意此路径不能再回到结点 u u u,如果结点 v v v的最长路径需要经过结点 u u u,那么只能取经过其它子结点的次长路径。
算法实现
- 任选一点
u
,自顶向下(结点u
到叶子结点)求出以每个结点为根向下走的最远距离和次远距离d1[u]
和d2[u]
,同时处理出p1[u]
和p2[u]
,表示结点u
是通过哪个子结点到达最远距离和次远距离。 - 任选一点
u
,自底向上(结点u
不经过父结点)求出每个结点向上走的路径的最远距离up[u]
。 - 打擂台,求所有结点到其它结点的最远距离
max(d1[i], up[i])
的最小值。
代码实现
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010, M = 2 * N, INF = 0x3f3f3f3f;
int n;
int h[N], w[M], e[M], ne[M], idx;
//d1[u],d2[u]分别表示结点u向下走的最远距离和次远距离
//p1[u],p2[u]分别表示结点u向下走最远距离和次远距离时经过的结点
//up[u]表示结点u向上走的最远距离
int d1[N], d2[N], p1[N], p2[N], up[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
//自顶向下求u到叶子结点的最远距离
int dfs_down(int u, int father)
{
d1[u] = d2[u] = -INF;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == father) continue;
//递归求子结点向下的最远距离
int d = dfs_down(v, u) + w[i];
if(d >= d1[u]) //如果比最远距离大
{
d2[u] = d1[u], p2[u] = p1[u];//更新次远距离
d1[u] = d, p1[u] = v; //更新最远距离
}
else if(d > d2[u]) //如果比次远距离大
{
d2[u] = d, p2[u] = v; //更新最远距离
}
}
//如果u是叶子结点
if(d1[u] == -INF) d1[u] = d2[u] = 0;
return d1[u];
}
//自底向上求u到其它结点的最长路径
void dfs_up(int u, int father)
{
for(int i = h[u]; ~i ; i = ne[i])
{
int v = e[i];
if(v == father) continue;
//如果父结点u向下的最长路径经过v
if(p1[u] == v)
{
//结点v向上走到最长路径为
//父结点u继续向上的的最长路径和u向下走的次长路径的最大值+边权
up[v] = max(up[u], d2[u]) + w[i];
}
else //如果父结点u向下的最长路径不经过v
{
up[v] = max(up[u], d1[u]) + w[i];
}
//处理好当前结点后,递归向下求v向上走的路径中的最远距离
dfs_up(v, u);
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i ++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs_down(1, -1);
dfs_up(1, -1);
int res = INF;
for(int i = 1; i <= n; i ++) res = min(res, max(d1[i], up[i]));
cout << res << endl;
return 0;
}