大臣的旅费

大臣的旅费

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

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

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

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

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

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

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

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

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

输入格式

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

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

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

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

输出格式

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

数据范围

1n105,
1Pi,Qin,
1Di1000

输入样例:

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

输出样例:

135

 

解题思路

两遍dfs解法

  根据题意,这是一个无环连通图,也就是一棵树。

  题目是问我们树上的最长路径是多少,也就是问我们树的直径。

  树的直径:一棵树上长度最长的路径。

  求树的直径的其中一种方法是用dfs:

  1. 任取一点x,求剩余的点到x的距离,并在这些点中找到一个到x距离最大的点y
  2. 求所有点到y的距离,到y最大的距离就是树的直径。

  我们从第一步找到一个距离x最远的点y一定是直径上的一个端点,因此根据第二步找到的距离y最远的点所构成的路径就是一条树的直径。下面证明这种做法是正确的。

  用反证法,假设y不是任何一条直径的端点。

  情况1,假设存在一条直径,与xy的路径有交点:

  假设vw是直径的两个端点,与xy的路径上有交点u

  因为y是距离x最远的一个点之一,因此有dxuydxuv,有公共的一段dxu,因此有duyduv。加上同一段duw,因此有dyuwdvuw。又因为dvw是一条直径,因此dyw也是一条直径,即y是直径的端点,与假设矛盾。

  情况2,假设存在一条直径,与xy的路径没有交点:

  假设vw是直径的两个端点,由于是一颗树(连通),因此在xy的路径中必然会有一个节点aa也可能是xy),与vw的路径中的一个节点b(同理b也可能是vw)是可以相互到达的(即连通)。

  因为y是距离x最远的一个点之一,因此有dxaydxabv,有公共的一段dxa,因此有daydabv。由于每一条路径的长度大于0dab>0,因此有day>dbvdyab>dbv。加上同一段dbw,因此有dyabw>dvw。又因为dvw是一条直径,而dyabw>dvw,矛盾。

  因此正确性得到证明,y必然是某条直径的端点。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 1e5 + 10, M = N << 1;
 7 
 8 int head[N], e[M], wts[M], ne[M], idx;
 9 int dist[N];
10 
11 void add(int v, int w, int wt) {
12     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
13 }
14 
15 void dfs(int src, int pre, int d) {
16     dist[src] = d;
17     for (int i = head[src]; i != -1; i = ne[i]) {
18         if (e[i] != pre) {
19             dfs(e[i], src, d + wts[i]);
20         }
21     }
22 }
23 
24 int main() {
25     memset(head, -1, sizeof(head));
26     
27     int n;
28     scanf("%d", &n);
29     for (int i = 0; i < n - 1; i++) {
30         int v, w, wt;
31         scanf("%d %d %d", &v, &w, &wt);
32         add(v, w, wt), add(w, v, wt);
33     }
34     
35     dfs(1, 0, 0);   // 任取一点1,求其余点到1的距离
36     
37     // 找到距离1最远的点maxv
38     int maxv = 1;
39     for (int i = 1; i <= n; i++) {
40         if (dist[i] > dist[maxv]) maxv = i;
41     }
42     
43     dfs(maxv, 0, 0);    // 求所有点到maxv的距离
44     
45     // 找到离maxv最远的距离,这个距离就是树的直径
46     int maxd = -1;
47     for (int i = 1; i <= n; i++) {
48         maxd = max(maxd, dist[i]);
49     }
50     printf("%lld", 10 * maxd + maxd * (maxd + 1ll) / 2);
51     
52     return 0;
53 }
复制代码

 

树形dp解法

  树形dp解法的原理看考这篇博文:旅游规划:https://www.cnblogs.com/onlyblues/p/15998496.html

  AC代码如下:

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1e5 + 10, M = N << 1;

int head[N], e[M], wts[M], ne[M], idx;
int d1[N], d2[N];   // d1[i]表示以i为最高点的最长路径,d2[i]表示以i为最高点的次长路径
int maxd;   // 树的直径

void add(int v, int w, int wt) {
    e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
}

void dfs(int src, int pre) {
    for (int i = head[src]; i != -1; i = ne[i]) {
        if (e[i] != pre) {
            dfs(e[i], src); // 求子节点e[i]的最长路径
            
            // 更新最大值和次大值
            int d = d1[e[i]] + wts[i];
            if (d > d1[src]) {
                d2[src] = d1[src];
                d1[src] = d;
            }
            else if (d > d2[src]) {
                d2[src] = d;
            }
        }
    }
    
    maxd = max(maxd, d1[src] + d2[src]);
}

int main() {
    memset(head, -1, sizeof(head));
    
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++) {
        int v, w, wt;
        scanf("%d %d %d", &v, &w, &wt);
        add(v, w, wt), add(w, v, wt);
    }
    
    dfs(1, -1);
    printf("%lld", 10 * maxd + maxd * (maxd + 1ll) / 2);
    
    return 0;
}
复制代码

 

参考资料

  AcWing 1207. 大臣的旅费(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/710/

posted @   onlyblues  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示