树的直径

定义

一个树的直径就是树上两个点的距离中最长的距离。

性质

  • 树的直径的路径不一定唯一。

  • 树的端点一定是度数为 1 的点。

  • 树的直径若有多条,则一定交汇于数的中心(以这个点为根,树的最大深度最小)。

  • 树上任意点 i,距离 i 最远的点,一定是树的直径的端点之一。

求法

  1. Floyd 暴力求索。

  2. 两遍 DFS,利用性质 4,从任意一个点开始 DFS,找到距离这个点最远的点 x,然后从 x 开始 DFS,找到最远的点 y,则点 xy 为数的直径的两端。不过在取最大值的时候,不能写当前长度大于最大长度,要大于等于,因为后面的可能长度一样,但是点要重新取。

例题

Luogu-B4016

思路

模板题,套上面的思路。

代码

#include <bits/stdc++.h>

using namespace std;

int n, x, mx;
bool vis[100005];
vector<int> g[100005];

void dfs(int u, int sum) {
	vis[u] = 1;
	if (sum >= mx) {
		mx = sum;
		x = u;
	}
	for (int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if (!vis[v]) {
			dfs(v, sum + 1);
		}
	}
	return;
}

int main() {
	cin.tie(0)->sync_with_stdio(false);
	cin >> n;
	for (int i = 1, x, y; i < n; ++i) {
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs(1, 0);
	fill(vis + 1, vis + n + 1, 0);
	dfs(x, 0);
	cout << mx;
	return 0;
}

HDU-2196

思路

方法一:

直接暴力 DFS。

方法二:

利用树的直径的性质 4,找出两个端点 xy,最远的点一定是它们当中的一个,所以从它们开始跑最短路 disxidisyi,然后答案就是 max(disxi,disyi)

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,x,mx,a,b,dis[N][2];
bool vis[N];
struct Node{
	int v,w;
};
vector<Node> g[N];
void dfs(int u,int sum)
{
	vis[u]=1;
	if(sum>=mx)
	{
		mx=sum;
		x=u;
	}
	for(int i=0;i<g[u].size();++i)
	{
		int v=g[u][i].v,w=g[u][i].w;
		if(!vis[v])
		{
			dfs(v,sum+w);
		}
	}
	return;
}
void dfs2(int u,int f)
{
	vis[u]=1;
	for(int i=0;i<g[u].size();++i)
	{
		int v=g[u][i].v,w=g[u][i].w;
		if(!vis[v])
		{
			dis[v][f]=dis[u][f]+w;
			dfs2(v,f);
		}
	}
	return;
}
signed main() 
{
	cin.tie(0)->sync_with_stdio(false);
	while(cin>>n)
	{
		for(int i=1;i<=n;++i)
		{
			g[i].clear();
			vis[i]=0;
			dis[i][0]=dis[i][1]=0;
		}
		a=0,b=0;
		for(int i=2,y,z;i<=n;++i)
		{
			cin>>y>>z;
			g[i].push_back({y,z});
			g[y].push_back({i,z});
		}
		mx=0,x=0;
		fill(vis+1,vis+n+1,0);
		dfs(1,0);
		a=x;
		fill(vis+1,vis+n+1,0);
		dfs(x,0);
		b=x;
		fill(vis+1,vis+n+1,0);
		dfs2(a,0);
		fill(vis+1,vis+n+1,0);
		dfs2(b,1);
		for(int i=1;i<=n;++i)
		{
			cout<<max(dis[i][0],dis[i][1])<<'\n';
		}
	}
	return 0;
}

Codeforces-734E

思路

我们先将一个颜色且联通的连通块缩成一个点,

然后,此时的树必定是黑白交替的,那么最终的答案就是 2

注意

两遍 DFS 的做法也有一个缺点,就是不能有负边权。

hack数据:

5
1 2 5
2 4 -10
4 5 20
1 3 4

正确答案:

19

实际输出:

20

树形DP求解

  1. 树的中心的最长链和次长链对应的端点就是树的直径的两端。

  2. 维护点 i 的最长路 dis1idis2i,取 \max(dis1_i + dis_2_i) 即树的直径。

  3. 除了共点 i,两条路径没有交点,树的中心要进行枚举。

  4. 可以处理负边权。

The End.

posted @   scguo_931664  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示