蓝桥杯 历届试题 大臣的旅费

题目描述

很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

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

J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。

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

输入

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

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

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

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

输出

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

样例输入

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

样例输出

135 

提示


输出格式



大臣J从城市4到城市5要花费135的路费。



思路:刚开始看到这道题第一想法是用dij()来求最长路径,但是到后来发现是求任意两点最长的距离。随后我有参考别人的代码,思路都是要用两遍DFS,即第一次找出最长路的一个端点,第二次便是从最长路的一个端点dfs出最长路;可能会有人问为什么这样求出来的距离就是两点之间最长的,下面给出证明的思路:

树的直径也就是最长路的证明:

假设 s-t这条路径为树的直径,或者称为树上的最长路

现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路

证明:

1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则

dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾

2   设u不为s-t路径上的点

    首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了

    所以现在又有两种情况了:

    1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)

    2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,

    则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾

找出来最长的路径,就可以用DFS来算出所需的费用了,下面附上AC代码:


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
struct node
{
    int now, cost;
};
vector<node>S[10010];
int vis[10010];
int a, b, c, n;
int temp;
long long ans = -99999;
void dfs(int s, int dist)
{
    if(ans < dist)
        ans = dist, temp = s;
    for(int i = 0; i < S[s].size(); i++)
    {
        node p = S[s][i];
        if(!vis[p.now]&&p.cost)
        {
            vis[p.now] = 1;
            dist+=p.cost;
            dfs(p.now, dist);
            vis[p.now] = 0;
            dist-=p.cost;
        }
    }
    return ;
}

int main()
{

    cin >> n;
    node p;
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i < n; i++)
        S[i].clear();
    for(int i = 0; i < n - 1; i++)
    {
        cin >> a >> b >>c;
        p.now = b;
        p.cost = c;
        S[a].push_back(p);
        p.now = a;
        S[b].push_back(p);
    }
    vis[1] = 1;
    dfs(1, 0);
    memset(vis, 0, sizeof(vis));
    vis[temp] = 1;
    ans = -99999;
    dfs(temp, 0);
    printf("%d \n", ((11+10+ans)*ans)/2);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值