给定一棵树,树中包含 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 0 5 ≤ c i ≤ 1 0 5 −10^5≤c_i≤10^5 −105≤ci≤105
输入样例
6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7
输出样例
22
根据题目描述,要找到一条路径,使得路径两端的点的距离最远,注意树中每条边的权值不同。我们可以任选一个点 u u u作为根结点,只要求出到 u u u距离最远的两个点,不妨设它们到 u u u的距离分别是 d 1 , d 2 d_1,d_2 d1,d2,那么 d 1 + d 2 d_1+d_2 d1+d2一定是树中最长路径。
反证法。假设到 u u u距离最远的两个点分别是 v 1 , v 2 v_1,v_2 v1,v2,它们到 u u u的距离分别是 d 1 , d 2 d_1,d_2 d1,d2,并且 d 1 + d 2 d_1+d_2 d1+d2不是树中最长路径。假设存在两个点 x , y x,y x,y,且路径 x y xy xy之间的距离是树中最长的路径。那么根据 x y xy xy是否经过 u u u点可以分为两种情况:
路径 x y xy xy经过 u u u点,如下图所示:
不妨设 x , y x,y x,y到结点 u u u的距离为 d 3 , d 4 d_3,d_4 d3,d4。根据假设,路径 x y xy xy之间的距离是树中最长的路径,那么 d 3 + d 4 > = d 1 + d 2 d_3+d_4>=d_1+d2 d3+d4>=d1+d2,而 v 1 , v 2 v_1,v_2 v1,v2是到 u u u的距离最远的两个点,所以 d 1 + d 2 > = d 3 + d 4 d_1+d_2>=d_3+d_4 d1+d2>=d3+d4,所以假设只有在它们相等时成立。
路径 x y xy xy不经过经过 u u u点,如下图所示:
因为树是连通的,所以 x x x和 y y y一定可以到达 u u u,不妨设 x , y x,y x,y之间的距离为 x y xy xy,它们到结点 u u u的距离为 d 3 , d 4 d_3,d_4 d3,d4,那么 d 3 + d 4 > x y d_3+d_4>xy d3+d4>xy。而 v 1 , v 2 v_1,v_2 v1,v2是到 u u u的距离最远的两个点,所以 d 1 + d 2 > = d 3 + d 4 d_1+d_2>=d_3+d_4 d1+d2>=d3+d4,那么 d 1 + d 2 > x y d_1+d_2>xy d1+d2>xy,因此假设不成立。
证毕。
使用邻接表实现一棵无根树,在树中任选一点u
作为根结点:
u
的最长距离d1
和次长距离d2
d1+d2
就是经过点u
的最长路径对于每个点,只求一次最长路径,所以时间复杂度为 O ( n ) O(n) O(n)
#include
#include
using namespace std;
const int N = 100010, M = 2 * N;
int h[N], e[M], ne[M], w[M], idx;
int n, ans;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
//计算经过u点的路径长度最大值
int dfs(int u, int father)
{
//dist表示从u往下走的最大长度
int dist = 0;
//到u点的最长距离和次长距离
//初始为0即可,负数表示路径不存在
int d1 = 0, d2 = 0;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == father) continue; //防止回上一层搜索
//求子结点的向下走的最大长度
int d = dfs(v, u) + w[i];
dist = max(dist, d);
//更新最长距离和次长距离
if(d >= d1) d2 = d1, d1 = d;
else if(d > d2) d2 = d;
}
//打擂台求经过u点的最长路径距离
ans = max(ans, d1 + d2);
return dist;
}
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(1, -1);
cout << ans << endl;
return 0;
}