树的直径
树上任意两节点之间最长的简单路径即为树的「直径」。
前置知识:树基础。
引入
显然,一棵树可以有多条直径,他们的长度相等。
可以用两次 DFS 或者树形 DP 的方法在
例题
SPOJ PT07Z, Longest path in a tree
给定一棵
做法 1. 两次 DFS
过程
首先从任意节点
显然,如果第一次 DFS 到达的节点
定理:在一棵树上,从任意节点
证明
使用反证法。记出发节点为
- 若
在 上:
有
- 若
不在 上,且 与 存在重合路径:
有
- 若
不在 上,且 与 不存在重合路径:
有
综上,三种情况下假设均会产生矛盾,故原定理得证。
负权边
上述证明过程建立在所有路径均不为负的前提下。如果树上存在负权边,则上述证明不成立。故若存在负权边,则无法使用两次 DFS 的方式求解直径。
实现
代码实现如下。
constexpr int N = 10000 + 10;
int n, c, d[N];
vector<int> E[N];
void dfs(int u, int fa) {
for (int v : E[u]) {
if (v == fa) continue;
d[v] = d[u] + 1;
if (d[v] > d[c]) c = v;
dfs(v, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
d[c] = 0, dfs(c, 0);
printf("%d\n", d[c]);
return 0;
}
如果需要求出一条直径上所有的节点,则可以在第二次 DFS 的过程中,记录每个点的前序节点,即可从直径的一端一路向前,遍历直径上所有的节点。
做法 2. 树形 DP
过程 1
我们记录当
树形 DP 可以在存在负权边的情况下求解出树的直径。
实现 1
代码实现如下:
constexpr int N = 10000 + 10;
int n, d = 0;
int d1[N], d2[N];
vector<int> E[N];
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]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", d);
return 0;
}
如果需要求出一条直径上所有的节点,则可以在 DP 的过程中,记录下每个节点能向下延伸的最长路径与次长路径(定义同上)所对应的子节点,在求
过程 2
这里提供一种只使用一个数组进行的树形 DP 方法。
我们定义
对于树的直径,实际上是可以通过枚举从某个节点出发不同的两条路径相加的最大值求出。因此,在 DP 求解的过程中,我们只需要在更新
实现 2
代码实现如下:
constexpr int N = 10000 + 10;
int n, d = 0;
int dp[N];
vector<int> E[N];
void dfs(int u, int fa) {
for (int v : E[u]) {
if (v == fa) continue;
dfs(v, u);
d = max(d, dp[u] + dp[v] + 1);
dp[u] = max(dp[u], dp[v] + 1);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", d);
return 0;
}
性质
若树上所有边边权均为正,则树的所有直径中点重合
证明:使用反证法。设两条中点不重合的直径分别为
有
习题
- CodeChef, Diameter of Tree
- Educational Codeforces Round 35, Problem F, Tree Destruction
- ZOJ 3820, Building Fire Stations
- CEOI2019/CodeForces 1192B. Dynamic Diameter
- IPSC 2019 网络赛,Lightning Routing I
- NOIP2007 提高组 树网的核
- SDOI2011 消防
- APIO2010 巡逻
创建日期: 2020年6月6日