大臣的旅费
大臣的旅费
很久以前, 王国空前繁荣。
为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费, 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。
同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。
所以,从一个城市马不停蹄地到另一个城市成了 最常做的事情。
他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第 千米到第 千米这一千米中(是整数),他花费的路费是 这么多。也就是说走 千米花费 ,走 千米要花费 。
大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数 ,表示包括首都在内的T王国的城市数。
城市从 开始依次编号, 号城市为首都。
接下来 行,描述 国的高速路(国的高速路一定是 条)。
每行三个整数 ,表示城市 和城市 之间有一条双向高速路,长度为 千米。
输出格式
输出一个整数,表示大臣 最多花费的路费是多少。
数据范围
,
,
输入样例:
5 1 2 2 1 3 1 2 4 5 2 5 4
输出样例:
135
解题思路
两遍dfs解法
根据题意,这是一个无环连通图,也就是一棵树。
题目是问我们树上的最长路径是多少,也就是问我们树的直径。
树的直径:一棵树上长度最长的路径。
求树的直径的其中一种方法是用dfs:
- 任取一点,求剩余的点到的距离,并在这些点中找到一个到距离最大的点。
- 求所有点到的距离,到最大的距离就是树的直径。
我们从第一步找到一个距离最远的点一定是直径上的一个端点,因此根据第二步找到的距离最远的点所构成的路径就是一条树的直径。下面证明这种做法是正确的。
用反证法,假设不是任何一条直径的端点。
情况,假设存在一条直径,与到的路径有交点:
假设,是直径的两个端点,与到的路径上有交点。
因为是距离最远的一个点之一,因此有,有公共的一段,因此有。加上同一段,因此有。又因为是一条直径,因此也是一条直径,即是直径的端点,与假设矛盾。
情况,假设存在一条直径,与到的路径没有交点:
假设,是直径的两个端点,由于是一颗树(连通),因此在到的路径中必然会有一个节点(也可能是或),与到的路径中的一个节点(同理也可能是或)是可以相互到达的(即连通)。
因为是距离最远的一个点之一,因此有,有公共的一段,因此有。由于每一条路径的长度大于,,因此有,。加上同一段,因此有。又因为是一条直径,而,矛盾。
因此正确性得到证明,必然是某条直径的端点。
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/15940402.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效