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

直接上求法:两遍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;
}

继续加油~


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值