大臣的旅费
大臣的旅费
很久以前,$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$ 条)。
每行三个整数 $P_{i},Q_{i},D_{i}$,表示城市 $P_{i}$ 和城市 $Q_{i}$ 之间有一条双向高速路,长度为 $D_{i}$ 千米。
输出格式
输出一个整数,表示大臣 $J$ 最多花费的路费是多少。
数据范围
$1 \leq n \leq 10^{5}$,
$1 \leq P_{i},Q_{i} \leq n$,
$1 \leq D_{i} \leq 1000$
输入样例:
5 1 2 2 1 3 1 2 4 5 2 5 4
输出样例:
135
解题思路
两遍dfs解法
根据题意,这是一个无环连通图,也就是一棵树。
题目是问我们树上的最长路径是多少,也就是问我们树的直径。
树的直径:一棵树上长度最长的路径。
求树的直径的其中一种方法是用dfs:
- 任取一点$x$,求剩余的点到$x$的距离,并在这些点中找到一个到$x$距离最大的点$y$。
- 求所有点到$y$的距离,到$y$最大的距离就是树的直径。
我们从第一步找到一个距离$x$最远的点$y$一定是直径上的一个端点,因此根据第二步找到的距离$y$最远的点所构成的路径就是一条树的直径。下面证明这种做法是正确的。
用反证法,假设$y$不是任何一条直径的端点。
情况$1$,假设存在一条直径,与$x$到$y$的路径有交点:
假设$v$,$w$是直径的两个端点,与$x$到$y$的路径上有交点$u$。
因为$y$是距离$x$最远的一个点之一,因此有$d_{xuy} \geq d_{xuv}$,有公共的一段$d_{xu}$,因此有$d_{uy} \geq d_{uv}$。加上同一段$d_{uw}$,因此有$d_{yuw} \geq d_{vuw}$。又因为$d_{vw}$是一条直径,因此$d_{yw}$也是一条直径,即$y$是直径的端点,与假设矛盾。
情况$2$,假设存在一条直径,与$x$到$y$的路径没有交点:
假设$v$,$w$是直径的两个端点,由于是一颗树(连通),因此在$x$到$y$的路径中必然会有一个节点$a$($a$也可能是$x$或$y$),与$v$到$w$的路径中的一个节点$b$(同理$b$也可能是$v$或$w$)是可以相互到达的(即连通)。
因为$y$是距离$x$最远的一个点之一,因此有$d_{xay} \geq d_{xabv}$,有公共的一段$d_{xa}$,因此有$d_{ay} \geq d_{abv}$。由于每一条路径的长度大于$0$,$d_{ab} > 0$,因此有$d_{ay} > d_{bv}$,$d_{yab} > d_{bv}$。加上同一段$d_{bw}$,因此有$d_{yabw} > d_{vw}$。又因为$d_{vw}$是一条直径,而$d_{yabw} > d_{vw}$,矛盾。
因此正确性得到证明,$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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/15940402.html