树的直径 —— 即一棵树的最长路 附题(大臣的旅费 by蓝桥杯)

来源:互联网 发布:kt交易师软件下载 编辑:程序博客网 时间:2024/06/06 08:38

直接上求法:两遍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相矛盾,反证成功。


在下面附上往年的一道蓝桥杯题目——大臣的旅费


历届试题 大臣的旅费  
时间限制:1.0s   内存限制:256.0MB
      
问题描述

很久以前,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
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式

大臣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;}

继续加油~


1 0