树的直径


树上任意两节点之间最长的简单路径即为树的「直径」。

前置知识:

树基础

引入

显然,一棵树可以有多条直径,他们的长度相等。

可以用两次 DFS 或者树形 DP 的方法在 时间求出树的直径。

例题

给定一棵 个节点的树,求其直径的长度。

做法 1. 两次 DFS

过程

首先从任意节点 开始进行第一次 DFS,到达距离其最远的节点,记为 ,然后再从 开始做第二次 DFS,到达距离 最远的节点,记为 ,则 即为树的直径。

显然,如果第一次 DFS 到达的节点 是直径的一端,那么第二次 DFS 到达的节点 一定是直径的一端。我们只需证明在任意情况下, 必为直径的一端。

定理:在一棵树上,从任意节点 开始进行一次 DFS,到达的距离其最远的节点 必为直径的一端。

使用反证法。记出发节点为 。设真实的直径是 ,而从 进行的第一次 DFS 到达的距离其最远的节点 不为 。共分三种情况:

  • 上:

y 在 s-t 上

,与 为树上任意两节点之间最长的简单路径矛盾。

  • 不在 上,且 存在重合路径:

y 不在 s-t 上,y-z 与 s-t 存在重合路径

,与 为树上任意两节点之间最长的简单路径矛盾。

  • 不在 上,且 不存在重合路径:

y 不在 s-t 上,y-z 与 s-t 不存在重合路径

,与 为树上任意两节点之间最长的简单路径矛盾。

综上,三种情况下假设均会产生矛盾,故原定理得证。

上述证明过程建立在所有路径均不为负的前提下。如果树上存在负权边,则上述证明不成立。故若存在负权边,则无法使用两次 DFS 的方式求解直径。

实现

代码实现如下。

const 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

过程

我们记录当 为树的根时,每个节点作为子树的根向下,所能延伸的最远距离 ,和次远距离 ,那么直径就是所有 的最大值。

树形 DP 可以在存在负权边的情况下求解出树的直径。

实现

代码实现如下。

const 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 的过程中,记录下每个节点能向下延伸的最远距离与次远距离所对应的子节点,之后再找到对应的 ,使得 ,即可分别沿着从 开始的最远距离和次远距离对应的子节点一路向下,遍历直径上所有的节点。

性质

若树上所有边边权均为正,则树的所有直径中点重合

证明:使用反证法。设两条中点不重合的直径分别为 ,中点分别为 。显然,

无负权边的树所有直径的中点重合

,与 为树上任意两节点之间最长的简单路径矛盾,故性质得证。

习题

贡献者:@WenzelTian@SBofGaySchool@Nathan@peterlits@XTh3G4p@mgt@Mout-sea@Shuhao@lychees

本页面最近更新:2/3/2023, 12:00:00 AM更新历史

发现错误?想一起完善? 在 GitHub 上编辑此页!

本页面的全部内容在 CC BY-SA 4.0SATA 协议之条款下提供,附加条款亦可能应用

评论

0 条评论
未登录用户


Copyright © 2016 - 2023 OI Wiki Team

最近更新:fd2ec2c, 2023-02-03

联系方式:Telegram 群组 / QQ 群组