蓝桥杯真题讲解【大臣的旅费】(树的直径、DFS)

大臣的旅费

很久以前,T 王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J 是 T 国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。

具体来说,一段连续的旅途里,第 1 千米的花费为 11,第 2 千米的花费为 12,第 3 千米的花费为 13,…,第 x 千米的花费为 x+10。

也就是说,如果一段旅途的总长度为 1 千米,则刚好需要花费 11,如果一段旅途的总长度为 2 千米,则第 1 千米花费 11,第 2 千米花费 12,一共需要花费 11+12=23。

J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式
输入的第一行包含一个整数 n,表示包括首都在内的 T 王国的城市数。

城市从 1 开始依次编号,1 号城市为首都。

接下来 n−1 行,描述 T 国的高速路(T 国的高速路一定是 n−1 条)。

每行三个整数 Pi,Qi,Di,表示城市 Pi 和城市 Qi 之间有一条双向高速路,长度为 Di 千米。

输出格式
输出一个整数,表示大臣 J 最多花费的路费是多少。

数据范围
1≤n≤105,
1≤Pi,Qi≤n,
1≤Di≤1000
输入样例:

5
1 2 2
1 3 1
2 4 5
2 5 4

输出样例:

135

分析:
由于题目说到不重复经过大城市,从首都到达每个大城市的方案都是唯一的。因此可以知道该图是一棵树,本题求的是树的直径

树的直径:树中长度最长的路径

1、任取一点x

2、找到距离x最远的点y

3、从y开始遍历,找到离y最远的点,与y最远的点的距离是树的直径

证明:y一定是树的直径的端点

假设y不是树的直径的端点,分两种情况,如图所示,其中uv是树的直径
在这里插入图片描述
情况1:xy与uv有交点,由于离x最远的点是y,因此

有 1 + 3 <= 3 + 4

即 3 <= 4

则 3 + 2 <= 4 + 2

由于 3 + 2是树的直径,因此4 + 2一定是树的直径,因此y不是树的直径的端点矛盾

情况2:xy与uv没有交点,由于离x最远的点是y,因此

有 1 + 2 >= 1 + 3 + 5

即 2 >= 3 + 5

即 2 > 5

则 2 + 3 > 5

则 2 + 3 + 5 > 4 + 5

由于 4 + 5是树的直径,但存在着一个长度更长的路径,因此y不是树的直径的端点矛盾

因此,y一定是树的直径的端点
dfs
1、通过深度优先遍历找到与x的最远距离的点y

2、再通过深度优先遍历找到与y的最远距离

注意:递归函数需要记录上一结点father

时间复杂度 O(n)

参考文献
小呆呆的题解(java)

cpp代码,用Vector实现图的存储

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define x first
#define y second

using namespace std;

const int N = 1e5 + 10;
vector<pair<int, int>> h[N]; 
int dist[N];

void dfs(int u, int father, int distance)
{
    dist[u] = distance;
    for(auto node : h[u])
        if(node.x != father)
            dfs(node.x, u, distance + node.y);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    int n;
    cin >> n;
    for(int i = 0; i < n - 1; i ++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        h[a].push_back({b, c});
        h[b].push_back({a, c});
    }
    //找到任意点x找到距离最远的点y
    dfs(1, -1, 0);
    
    int u = 1;
    for(int i = 1; i <= n; i ++)
    {
        if(dist[u] < dist[i])
            u = i;
    }
    //找到离y最远的点的距离
    dfs(u, -1, 0);
    for(int i = 1; i <= n; i ++)
        if(dist[u] < dist[i])
            u = i;
    
    int s = dist[u];
    cout << 10 * s + s * (s + 1) / 2 << endl;
    return 0;
}
  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值