C++

木の直径を求める

ISO_C++_Logo

グラフ(木)の直径を求めるプログラムを紹介します。

定義と手順

木の最も遠い頂点間の距離を木の直径と定義します。

木の直径は、以下の手順で求めることができます。

  • ある頂点 $a$ から、もっとも遠い頂点 $b$ を求める。
  • その頂点 $b$ から、もっとも遠い頂点 $c$ を求める。
  • 頂点 $b$ と頂点 $c$ の距離が木の直径となる。

ある頂点からもっとも遠い頂点は、BFS(幅優先探索:Breadth-First search)でも、DFS(深さ優先探索:Depth-First Search)でも求めることができます。

アルゴリズムのテスト

AIZU ONLINE JUDGE で出題されている問題でアルゴリズムをテストします。以下が問題のリンク先です。

GRL 5_A問題 Diameter of a Tree

この問題を、BFS を使って解くプログラムは以下です。手順で記載したように BFS を2回使います。

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

struct Edge {
	int to;
	int weight;
	Edge(int t, int w) : to(t), weight(w) { }
};

int main()
{
	int n;
	cin >> n;
	vector<vector<Edge>> G(n);
	for (int i = 0; i < n - 1; ++i) {
		int s, t, w;
		cin >> s >> t >> w;
		G[s].push_back(Edge(t, w));
		G[t].push_back(Edge(s, w));
	}

	vector<int> dist(n, -1);
	queue<int> que;
	que.push(0);
	dist[0] = 0;
	int max_v = 0;
	int max_dist = 0;
	while (!que.empty()) {
		int now = que.front();
		que.pop();
		for (auto next_v : G[now]) {
			if (dist[next_v.to] == -1) {
				que.push(next_v.to);
				dist[next_v.to] = dist[now] + next_v.weight;
				if (dist[next_v.to] > max_dist) {
					max_v = next_v.to;
					max_dist = dist[next_v.to];
				}
			}
		}
	}

	dist.assign(n, -1);
	que.push(max_v);
	dist[max_v] = 0;
	max_dist = 0;
	while (!que.empty()) {
		int now = que.front();
		que.pop();
		for (auto next_v : G[now]) {
			if (dist[next_v.to] == -1) {
				que.push(next_v.to);
				dist[next_v.to] = dist[now] + next_v.weight;
				max_dist = max(max_dist, dist[next_v.to]);
			}
		}
	}

	cout << max_dist << endl;

	return 0;
}

次に DFS を使って解くプログラムは以下です。

#include <iostream>
#include <vector>

using namespace std;

struct Edge {
	int to;
	int weight;
	Edge(int t, int w) : to(t), weight(w) { }
};

vector<vector<Edge>> G;
vector<int> dist;
int max_v = 0;
int max_dist = 0;

void dfs(int v, int par, int d)
{
	dist[v] = d;
	if (dist[v] > max_dist) {
		max_v = v;
		max_dist = dist[v];
	}
	for (auto next_v : G[v]) {
		if (next_v.to != par) {
			dfs(next_v.to, v, d + next_v.weight);
		}
	}
}

int main()
{
	int n;
	cin >> n;
	G.resize(n);
	for (int i = 0; i < n - 1; ++i) {
		int s, t, w;
		cin >> s >> t >> w;
		G[s].push_back(Edge(t, w));
		G[t].push_back(Edge(s, w));
	}

	dist.assign(n, -1);
	dfs(0, -1, 0);

	dist.assign(n, -1);
	dfs(max_v, -1, 0);

	cout << max_dist << endl;

	return 0;
}

どちらもACと判定されました。

最後に

この記事を書くことで、木の直径に対する理解が深まりました。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA