LoginSignup
22
6

More than 3 years have passed since last update.

木の直径を求める

Last updated at Posted at 2019-05-23
  • まちがってたらごめんね。変なところあったら指摘してね。

木の直径とは

  • 木における最大頂点間距離のこと

double-sweepで求める

アルゴリズム

  • 木として$T=(V, E)$が与えられる
  • ある頂点$u \in V$を選び、$u$を始点として最大距離となる終点$v \in V$を探し出す。
  • 先ほど探し出した頂点$v$を始点として最大距離となる終点$w \in V$を探し出す。
  • このとき、経路$v, w$の距離は木の直径となる。

木の直径_1.png

証明

  • 経路$v, w$が木での最長の経路となることを証明する。証明には背理法を使う。
  • 経路$v, w$が木の最長経路とならないと仮定する。つまり、経路$v, w$より長い経路$a, b \in V$が存在すると仮定する。
  • 木は頂点$a, b$の場所で大きく2通りに場合分けできる。
  • 1つ目は$L = LCA(a, b)$が経路$u, v$上にあるときである。この場合を細かく(i), (ii)に分けて考える(図を参照)。

  • (i)について考える。以下の式より、$|ab|$以上となる$|vb|$が存在する。よって、この場合経路$a, b$は最長の経路ではない

|ab| = |aL| + |bL| \\
|aL| \leq |vL| \\
|ab| = |aL| + |bL| \leq |vL| + |bL| = |vb|
  • (ii)について考える。(i)と同様に以下の式より、$|ab|$以上となる$|vb|$が存在する。よって、この場合経路$a, b$は最長の経路ではない
|ab| = |aL| + |bL| \\
|aL| \leq |vL| \\
|ab| = |aL| + |bL| \leq |vL| + |bL| = |vb|

木の直径_2.png

  • 2つ目は$L$が経路$u, v$上にないときである。これを細かくして(iii), (iiii)に分けて考える。
  • (iii)について考える。$L_2=LCA(v, L)$とする。以下の式より、$ab$以上となる長さ$vb$が存在する。よって、この場合経路$a, b$は最長の経路ではない
|vL_2| \geq |aL_2| \\
|vL_2| > |aL| \\
|vL| > |aL| \\
|ab| = |aL| + |bL| < |vL| + |bL| = |vb|
  • (iiii)について考える。以下の式より、以下の式より、$ab$以上となる長さ$vb$が存在する。よって、この場合経路$a, b$は最長の経路ではない
|uv| \geq |ua| \\
|uv| > |aL| \\
|vL| > |aL| \\
|ab| = |aL| + |bL| < |vL| + |bL| = |vb|

木の直径_3.png

  • はじめに経路$a, b$が最長の経路となると仮定した。しかし、(i)~(iiii)より、経路$a, b$が最長の経路ではないと証明した。経路$v, w$より長い経路$a, b$が存在するという仮定に矛盾が生じた。よって、木の直径は経路$v, w$の長さとなる。

コード

  • getTreeDiameter()を呼べば木の直径とその始点、終点が1通り求められる。
const int maxV = 111111;
vector<int> G[maxV]; // 頂点情報のみのグラフ

// treeDFS(親, 現在地, 根から現在地までの距離, 根からの最大の距離, 根から最大の距離となる頂点
void treeDFS(int from, int current, int dist, int &maxDist, int &maxVertex) {
    // 距離と終点を更新
    if (dist > maxDist) {
        maxDist = dist;
        maxVertex = current;
    }

    for (auto to : G[current]) {
        // 逆流を防ぐ
        if (to == from) continue;
        treeDFS(current, to, dist + 1, maxDist, maxVertex);
    }
}

void getTreeDiameter() {
    int start = 0, end = 0, maxDist = 0;
    treeDFS(-1, start, 0, maxDist, end);
    start = end, end = 0, maxDist = 0;
    treeDFS(-1, start, 0, maxDist, end);
    printf("start: %d, end: %d, diameter: %d\n", start, end, maxDist);
}

直径を使って解く問題

メモ

  • 直径は木DPでも求められるみたいなんだけど、木DPよくわからん。この辺の記事が良さげ。全方位木DPってやつらしい。
  • double-sweepってアルゴリズム、ABC019のスライドに載ってたけどググっても出てこない。double-sweepは俗称みたいなもの?
  • wからxへの最大の経路を$|wx|$とする。$|vw| \leq |wx|$だから$|vw|$が最大にならないのでは?と思ったけどそんなことはなかった。$a, b$は任意の頂点を表すので、$|ab|$が最大でないことを示せば$|vw|$が最大ということになった。
  • 直径を使って解く問題、他にあったら教えてほしいです
22
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
6