求树的的直径与求图的最长路是一样的。
一对点之间经过的所有节点和边会组成一条链,最长的一条链就是最长路,在树中,最长链就是树的直径。
对于解决这样的问题。我们一般用的是双DFS法,具体操作如下:
- 随便找一个点 x x x,搜索出离这个点最远的点 y y y 。
- 从 y y y 开始搜索,找出离 y y y 最远的点 z z z 。
- z z z 和 y y y 就是直径的两个端点。
分析一下这个思路是否满足所有情况:
- 如果 x 在 y 到 z 的路径上,假设这条路不是直径,那就说明存在一个离 x 更远的点 p,那这样的话第一遍DFS会在 p 处停止,而不是在 y 处。所以不存在比 y-z 更长的链。
- 如果 x 不在 y 到 z 的路径上,则有:
- 如果 x 到与其最远的点 p 这条链 x-p 与直径相交,这就意味着交点到 y 或 z 的距离小于到 x 或 p 的距离,这样的话有悖于“直径最长”这一性质。
- 如果 x-y 与真正的直径没有交点,那这样的话从直径离 x-y 近的一端引一条路到 x-y 上,这条路会长于直径,再次有悖。
代码实现如下:
每次输入x, y, d,表示从 x 到 y 距离为 d。
#include<bits/stdc++.h>
using namespace std;
struct Edge {
int to, dis;
Edge(int x, int y) {
to = x, dis = y;
}
};
vector<Edge> mp[10000];
int fl[10000];
void addEdge(int x, int y, int dis) {
mp[x].push_back(Edge(y, dis));
mp[y].push_back(Edge(x, dis));
}
int maxDis = 0, maxNum = 1;
void dfs(int now, int sum) {
if (sum > maxDis) {
maxDis = sum;
maxNum = now;
}
for (int i = 0; i < mp[now].size(); i++) {
if (fl[mp[now][i].to] != 1) {
fl[mp[now][i].to] = 1;
dfs(mp[now][i].to, sum + mp[now][i].dis);
}
}
}
int main() {
int n, x, y, d;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> x >> y >> d;
addEdge(x, y, d);
}
dfs(1, 0);
memset(fl, 0, sizeof fl);
int t = maxNum;
maxNum = 0, maxDis = 0;
dfs(t, 0);
cout << "\n" << t << "\t" << maxNum;
return 0;
}