蓝桥双周赛:串门

https://www.lanqiao.cn/problems/5890/learning/?contest_id=145icon-default.png?t=N7T8https://www.lanqiao.cn/problems/5890/learning/?contest_id=145

问题描述

过年了,小蓝想要回家串门。

蓝桥村可以抽象为 n 个节点, n−1 条边的一棵树,每条边有边权长度 wi​。

小蓝可以选择任意一个点作为起点,然后选择一条路径,可以访问每个节点至少一次。他想知道最短的路径长度是多少。

输入格式

第一行输入一个整数 n,表示节点数量。

接下来 n−1 行,每行三个整数vi​,ui​,wi​,表示(vi​,ui​) 存在一条wi​ 的边。

输出格式

输出一个整数,表示最短路径。

样例输入

4
1 2 3
1 3 4
1 4 5

样例输出

15

说明

路径为:4→51→32→31→434→51→32→31→43,路径和值为 1515。

保证输入数据是一棵树。

不得不说,关于树的各种路径问题一直是我的薄弱点。这道题我一开始看的时候完全没有思路,但看了两篇题解,思路一下就被打开了,看来的却是缺少这方面的训练。

回到正题。我们可以思考,若是要遍历整棵树的所有节点并在最后回到原点的话,这棵树的每条路径一定是要走两遍的,也就是总路径之和为两倍树的路径长之和。但这道题不需要回到原点,也就是说,我们可以简化他为从树的一端到另一端。在纸上画几棵树,然后拿手比划着遍历一下,你就会发现,一定是有一条路径只需要走一遍的,并且这条路径一定以树的顶点为中间的某个点,也就是一定会经过树的顶点,我们将之称之为树的直径。而其他所有路线都会作为这条直径的分叉树,也就是走过再回来的路径和一定是其路径长的两倍。因此,我们只需要找到最长的那条直径作为直用走一遍的直径,我们得到的也就是遍历所需的最短路了。

因此我们可以这样:先把所有边权值加起来乘二存起来,然后用dfs,找到树的顶点的最长权值与第二长的权值,然后用前者减去后者就是我们的答案了。

关于这道题的图的存储的数据结构,我们选择链式前向星。代码如下:

#include <iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int N = 1e5 + 10;

struct edge
{
    int to, w, next;//分别代表这条边的终点,权值以及同起点的下一条边编号
};
edge e[N];
int head[N];//e数组存储边,head数组存储以i点为起点的第一条边

int DFS(int i)
{
    int m1 = 0, m2 = 0;
    int num = head[i];
    while (num != -1)//枚举以该点为起点的每一条边
    {
        int dis = DFS(e[num].to) + e[num].w;
        if (dis > m1)
            m2 = m1, m1 = dis;
        else if (dis > m2)
            m2 = dis;
        num = e[num].next;
    }
    return m1 + m2;
}

signed main()
{
    int n;cin >> n;
    int all = 0;
    //有了以某个点为起点的这第一条边与这条边的上一条边的编号,就可以像遍历链表那样遍历以该点为起点的所有边
    for (int i = 0;i < N;i++)
        head[i] = -1;
    for (int i = 1;i <= n - 1;i++)
    {
        int a, b, c;// 起点,终点,权值
        cin >> a >> b >> c;
        int aa = min(a, b), bb = max(a, b);//
        e[i].to = bb, e[i].w = c, e[i].next = head[aa];;
        head[aa] = i;
        all += c;
    }
    all *= 2;
    cout << all - DFS(1);

    return 0;
}

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值