概念:什么是树的直径

树上距离最远的两个点所连接的路径,叫做树的直径,显而易见的,一个树可以有多个直径。

实现:怎么写树的直径

Dfs深搜两遍

思路很简单,直接去找最远的两个点。先从任意点开始,找到距离他最远的点,记作 $x$ ;然后从 $x$ 开始找最远的点,记作 $y$ 。$x$ 和 $y$ 连接后的路径就是直径。但是这种深搜的做法无法解决负边权的树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<bits/stdc++.h>
using namespace std;

int n;
vector<pair<int , int > >e[30010];

int dis[30010], vis[30010];

void Input(){
scanf("%d", &n);
int u, v, w;
for(int i=1; i<n; i++){
scanf("%d%d%d", &u, &v, &w);
e[u+1].push_back(make_pair(v+1, w));
e[v+1].push_back(make_pair(u+1, w));
}
}

void Dfs(int x){
vis[x]=1;
for(auto &p:e[x]){
if(vis[p.first]) continue;
dis[p.first]=dis[x]+p.second;
Dfs(p.first);
}
}

void Work(){
vis[1]=1;
Dfs(1);
int x=1;
for(int i=1; i<=n; i++){
if(dis[i]>dis[x]) x=i;
}
memset(dis, 0, sizeof(dis));
memset(vis, 0, sizeof(vis));
vis[x]=1;
Dfs(x);
x=1;
for(int i=1; i<=n; i++){
if(dis[i]>dis[x]) x=i;
}
printf("%d", dis[x]);
}

int main(){
Input();
Work();
return 0;
}

树形DP

我们记录当 $1$ 为树的根时,每个节点作为子树的根向下,所能延伸的最长路径长度 $d_1$ 与次长路径(与最长路径无公共边)长度 ,$d_2$ 那么直径就是对于每一个点,该点 $d_1 + d_2$ 能取到的值中的最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
void dfs(int u, int fa) {
d1[u] = d2[u] = 0;
for (int v : E[u]) {
if (v == fa) continue;
dfs(v, u);
int t = d1[v] + 1;
if (t > d1[u])
d2[u] = d1[u], d1[u] = t;
else if (t > d2[u])
d2[u] = t;
}
d = max(d, d1[u] + d2[u]);
}

实践:树的直径经典例题