C++题解:树的最长路径

题目描述

给定一棵树,树中包含 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 1n10000,
1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1ai,bin,
− 1 0 5 ≤ c i ≤ 1 0 5 −10^5≤c_i≤10^5 105ci105

输入样例

6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7

输出样例

22

算法思想(DFS)

根据题目描述,要找到一条路径,使得路径两端的点的距离最远,注意树中每条边的权值不同。我们可以任选一个点 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点可以分为两种情况:

  1. 路径 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,所以假设只有在它们相等时成立。

  2. 路径 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作为根结点:

  • 通过DFS求出所有点到u的最长距离d1和次长距离d2
  • d1+d2就是经过点u的最长路径
  • 打擂台求出所有最长路径中的最大值

时间复杂度

对于每个点,只求一次最长路径,所以时间复杂度为 O ( n ) O(n) O(n)

代码实现

#include <iostream>
#include <cstring>
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

少儿编程乔老师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值