树的直径 | Diameter of a Tree | C/C++实现

问题描述

请求出权值非负的无向树T的直径。我们将树的最远结点之间的距离称为树的直径。

输入:
输入按照以下形式给出
n n n
s 1 s_1 s1 t 1 t_1 t1 w 1 w_1 w1
s 2 s_2 s2 t 2 t_2 t2 w 2 w_2 w2

s n − 1 s_{n-1} sn1 t n − 1 t_{n-1} tn1 w n − 1 w_{n-1} wn1
第1行输入表示树结点数的整数n。树的各结点编号分别为0到n-1。
接下来n-1行输入树的边。 s i s_i si t i t_i ti表示第 i 条边的两个端点, w i w_i wi表示第 i 条边的权值(距离)。
输出:
输出直径,占1行。
限制:
1 ≤ n ≤ 100000
0 ≤ w i w_i wi ≤ 1000

输入示例

第一组
4
0 1 2
1 2 1
1 3 3
第二组
4
0 1 1
1 2 2
2 3 4

输出示例

第一组
5
第二组
7

讲解

通过下述算法可以相对简单地求解树的直径
1.任选一结点s,求到s最远的结点x。
2.求到x最远的结点y。
3.报告结点x与y的距离,即树的直径。

该算法的严密证明过于繁琐,所以只介绍一种方法来验证其合理性。设结点a到结点b的距离为d(a, b)。

首先,根据树的性质可知如下内容:
x、y均为叶结点。如果两个结点的距离是直径,那么它们必然都是叶结点。
两个不同结点之间仅存在一条路径。
各权边值非负。

现假设另外两个结点u、v的距离d(u, v)为树的直径,而上面的算法得出的直径仍为d(x, y)。

AC代码如下

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
#define MAX 100000
#define INFTY (1<<30)

class Edge{
	public:
		int t, w;
		Edge(){}
		Edge(int t, int w): t(t), w(w) {}
}; 

vector<Edge> G[MAX];
int n, d[MAX];

bool vis[MAX];
int cnt;

void bfs(int s){
	for(int i = 0; i < n; i++) d[i] = INFTY;
	queue<int> Q;
	Q.push(s);
	d[s] = 0;
	int u;
	while(!Q.empty() ){
		u = Q.front(); Q.pop();
		for(int i = 0; i < G[u].size(); i++){
			Edge e = G[u][i];
			if(d[e.t] == INFTY){
				d[e.t] = d[u] + e.w;
				Q.push(e.t);
			}
		}
	}
}

void solve(){
	//从任选的结点s出发,选择距离s最远的结点tgt
	bfs(0);
	int maxv = 0;
	int tgt = 0;
	for(int i = 0; i < n; i++){
		if(d[i] == INFTY) continue;
		if(maxv < d[i]){
			maxv = d[i];
			tgt = i;
		} 
	} 
	
	//从tgt出发,求结点tgt到最远节点的距离maxv
	bfs(tgt);
	maxv = 0;
	for(int i = 0; i < n; i++){
		if(d[i] == INFTY) continue;
		maxv = max(maxv, d[i]);
	} 
	
	cout<<maxv<<endl;
}	 

int main(){
	int s, t, w;
	cin>>n;
	
	for(int i = 0; i < n-1; i++){
		cin>>s>>t>>w;
		
		G[s].push_back(Edge(t, w));
		G[t].push_back(Edge(s, w));
	}
	solve();
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值