直接上求法:两遍bfs就可以求得最长路的两个端点。
参考结论:从任意一点出发能搜到的最远的点一定是最长路两个端点中的一个。
(注意:最远可以是权值最远,也可以是边数最多,总之思想是相通的,求法也一样)
证明如下:
首先,设a -- b 是最长路
① 设点u为最长路a -- b上的一个点,所以从该点出发,所能到达的最远的点肯定是a,b之一。
② 设点u不是最长路a -- b上的一点,这里还有一个小结论,就是u到所能到达的最远的点v肯定会与该树的最长路相交,在这先设一个点t为最长路a -- b上的一点且该点是u到最远点路径与a -- b最长路的交点,所以很容易想到了,再假如 a是u到达最远的点。
d[u -- v] = d[u -- t] + d[t -- a].
反证法证明:如果u到最远点v与a--b无交点,即 dis[u -- v] >d[u -- t] + d[t -- a].
d[u -- v] + d[u -- t] + d[t -- b] > d[u -- t] + d[t -- a] + d[t -- b] (== d[a -- b]); 此时与最长路是a -- b相矛盾,反证成功。
在下面附上往年的一道蓝桥杯题目——大臣的旅费
很久以前,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最多花费的路费是多少。
1 2 2
1 3 1
2 4 5
2 5 4
大臣J从城市4到城市5要花费135的路费。
下面贴代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct node{
int u, v, w, next;
}e, edge[1000005];
int head[100005], book[100005];
int n, no;
void init(){
no = 1;
for(int i = 1; i <= n; ++i) {
head[i] = -1;
book[i] = 0;
}
}
void add(int u, int v, int w){ //使用前向星来存储边的信息
edge[no].u = u;
edge[no].v = v;
edge[no].w = w;
edge[no].next = head[u];
head[u] = no++;
}
int bfs(int x, long long &sum){
int k, ans, max, va;
max = 0;
memset(book ,0, sizeof book);
queue<node> q;
e.v = x;
e.w = 0;
q.push(e);
while(!q.empty()){ //通过队列不断更新,最大的距离,并记录当时的点
e = q.front();
q.pop();
if(book[e.v]) continue;
if(max < e.w){
max = e.w;
ans = e.v;
}
va = e.w;
book[e.v] = 1;
k = head[e.v];
while(k != -1){
if(book[edge[k].v] == 0){
e.w = va+edge[k].w; //用出队的权值+该点所能抵达的点的权值
e.v = edge[k].v;
e.u = edge[k].u;
q.push(e);
}
k = edge[k].next;
}
}
sum = max;
return ans;
}
int main(){
int i, u, v, w, a, b, ans;
long long sum;
cin >> n;
init();
for(i = 1; i < n; ++i){
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
if(n == 1) printf("0\n");
else{
sum = 0;
a = bfs(1, sum); //两次bfs获取最长路两端点,并用sum作为输出参数获得最长路的大小
sum = 0;
b = bfs(a, sum);
printf("%lld\n", sum*10+sum*(sum+1)/2);
}
return 0;
}
继续加油~