ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树的直径

2022-07-27 14:34:16  阅读:163  来源: 互联网

标签:结点 idx int 路径 ge 直径


树的直径

题目描述

树中两点间的不重复经过的边和点道路称为两点的路径,路径的长度(路径上所经边的长度和)称为两点的距离。圆的直径是一个圆的最长的一条弦,而树的直径是树中两点间最长的路径。通常用一个无序点对(x,y)表示一棵树的直径。
现在输入一个有n个结点的树,结点编号为1到n,假设结点1为根。试求树的直径。

输入格式

输入包含n+1行,第一行为1个正整数n,表示树的结点个数。
接下来n-1行,每行两个整数x和y,表示结点x和y之间有一条边,其中x是y的父亲。

输出格式

输出只有一个整数,为树的直径。

数据范围

n ≤ 1 0 5 n≤10^5 n≤10
5

输入样例

6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7

输出样例

22

算法思想

在树中任取一个结点 t,求到 t距离最远的结点 u,接着求到 u距离最远的结点 v,那么结点 u 和 v 之间的路径就是树的一条直径。证明
根据上述思想,只需证明第一次找到的结点 u 一定是某条直径的端点即可。

反证法:假设距离 t最远的结点 u不是某条直径的端点,并且存在某条直径,其端点分别为 x 、 y 。那么 t 到 u 之间的路径和 x 到 y 之间的路径,根据有无公共结点,可分为相交和不相交两种情况,如下图所示:

不相交的情况
image
因为树是连通的,所以从结点 t t t一定存在一条路径能够到达结点 y y y,如下图所示:
image

由于 u u u是到 t t t距离最远的点,所以 ① + ② ≥ ① + ⑤ + ④ ①+②ge①+⑤+④ ①+②≥①+⑤+④,那么 ② ≥ ⑤ + ④ ②ge⑤+④ ②≥⑤+④, ② + ⑤ ≥ ④ ②+⑤ge④ ②+⑤≥④。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ ③+④一定是树中最长的路径,而 ② + ⑤ + ③ ≥ ③ + ④ ②+⑤+③ge③+④ ②+⑤+③≥③+④,所以如结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。

相交的情况
image

由于 u 是到 t 距离最远的点,所以 ① + ② ≥ ① + ④ ①+②ge①+④ ①+②≥①+④,即 ② ≥ ④ ②ge④ ②≥④,那么 ③ + ② ≥ ③ + ④ ③+②ge③+④ ③+②≥③+④。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ ③+④一定是树中最长的路径,如果结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。
证毕。

算法实现

使用邻接表实现一棵无根树,在树中任取一个结点t:
通过DFS求出所有点i到t的距离dep[i]
打擂台,求出到t距离最远的结点u
使用相同的方法求出到u距离最远的结点v
dep[v]就是树的直径时间复杂度
需要对所有节点遍历一次,时间复杂度为 O ( n ) 代码实现一(简单模拟)

#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx;
int n, dep[N];
//邻接表,向链表头部插入新结点
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

//求以u为根的子树中所有结点到根结点的深度
int dfs(int u, int fa, int depth)
{
dep[u] = depth;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
dfs(v, u, depth + 1);
}
}

int main()
{
cin >> n;
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
//需要处理为无根树
add(a, b), add(b, a);
}
//任选一点u作为根结点,求所有点到u的距离
int u = 1;
dfs(u, 0, 0);
//求距离最远的点u
for(int i = 1; i <= n; i ++)
{
if(dep[i] > dep[u])
{
u = i;
}
}
//以u作为根结点,求所有点到u的距离
dfs(u, 0, 0);
//求距离最远的点u
for(int i = 1; i <= n; i ++)
{
if(dep[i] > dep[u])
{
u = i;
}
}
cout << dep[u] << endl;
return 0;
}

代码实现二(求最长、次长深度)

#include <iostream>
#include <cstring>
using namespace std;

const int N = 100010, M = 2 * N;
int h[N], e[M], ne[M], w[M], idx;
int n, ans;

void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
//求以u为根的子树的最大深度
int dfs(int u, int father)
{
int d1 = 0, d2 = 0; //d1表示最大深度,d2表示次大深度
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == father) continue;
int d = dfs(v, u) + 1;
if(d >= d1) d2 = d1, d1 = d;
else if(d > d2) d2 = d;
}
ans = max(ans, d1 + d2);
return d1;
}

int main()
{
cin >> n;
//初始化邻接表头指针
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
//实现无根树,每条边保存两次
add(a, b), add(b, a);
}
dfs(1, -1);
cout << ans << endl;
return 0;
}

标签:结点,idx,int,路径,ge,直径
来源: https://www.cnblogs.com/aska00/p/16524739.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有