幅優先探索 (BFS) の 色塗りで、 つながった 2 人を 別の 組に 分けられるか調べる
人と
海外で定番の問題リスト 4 本には入っていません
問題
運動会のadj[i] は、
どの 2 人も、true、false を
- 片方の
組が 0 人に なってもかまいません - 関係が 1 つも
無い 人は、 どちらの 組に 入れてもかまいません - 参加者の
数は 1 以上 100 以下です。 自分 自身との 関係や、 同じ 2 人の 2 重の 関係は ありません
入力と出力の例
例 1
0 と 3 を
例 2
0、
例 3
0 と 1 の 2 人と、
面接での聞かれ方
面接では、
- 「無向グラフが
二部グラフか どうかを 判定してください」 - 書き終えると
「グラフが いく つかに 離れていたら?」と、 全部の まとまりを 調べたかを 聞かれます - 「なぜ 2 色で
塗れない ことと、 奇数の 長さの 輪が ある ことが 同じなのですか?」と、 理由を 聞かれる ことがあります
グラフが 1 つに
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、
関わる学び
グラフ探索は、
まず 考える手順
段 1
全員の
段 2
まだ組のfalse です。
段 3
関係でtrue です。
段 4
塗れない
解答
Python
Python
from collections import deque
def two_teams(adj):
team = [-1] * len(adj)
for s in range(len(adj)):
if team[s] != -1:
continue
ok = paint(adj, team, s)
if not ok:
return False
return True
def paint(adj, team, s):
# s から届く人を 0 と 1 で塗る
team[s] = 0
q = deque([s])
while q:
v = q.popleft()
t = 1 - team[v]
for u in adj[v]:
if team[u] == -1:
team[u] = t
q.append(u)
elif team[u] != t:
return False
return TrueJavaScript
JavaScript
function twoTeams(adj) {
const team = adj.map(
() => -1);
for (const s of team.keys()) {
if (team[s] !== -1) {
continue;
}
if (!paint(adj, team, s)) {
return false;
}
}
return true;
}
// s から届く人を 0 と 1 で塗る
function paint(adj, team, s) {
team[s] = 0;
const q = [s];
for (const v of q) {
const t = 1 - team[v];
for (const u of adj[v]) {
if (team[u] === t) {
continue;
}
if (team[u] !== -1) {
return false;
}
team[u] = t;
q.push(u);
}
}
return true;
}Go
Go
func twoTeams(
adj [][]int,
) bool {
team := make([]int, len(adj))
for i := range team {
team[i] = -1
}
for s := range adj {
if team[s] != -1 {
continue
}
if !paint(adj, team, s) {
return false
}
}
return true
}
// s から届く人を 0 と 1 で塗る
func paint(
adj [][]int,
team []int, s int,
) bool {
team[s] = 0
q := []int{s}
for i := 0; i < len(q); i++ {
v := q[i]
t := 1 - team[v]
for _, u := range adj[v] {
if team[u] == -1 {
team[u] = t
q = append(q, u)
} else if team[u] != t {
return false
}
}
}
return true
}組は 0 (赤) と 1 (白)、1 - team[v] が、
計算量
- 時間は O(V + E) です。
V は 人の 数、 E は 関係の 数です。 どの 人も 1 回だけ塗られて列に 並び、 どの 関係も 両側から 1 回ずつ見ます - 使う
記憶は O(V) です。 組の 並びと列です
よく ある間違い
- 0 番の
人から しか 始めない。 例 3 のように 離れた 人が いると、 その 人の まとまりを 調べずに 終わります。 今は 分けられても、 離れたまと まりに 3 人の 輪が あれば 見落とします - 塗った
隣の 色を 確かめない。 塗られていない 隣だけを 見ると、 同じ 色どうしの 関係に 気付かず、 例 2 で trueを返します - 列から
取り出すときに塗る。 同じ 人が 2 回列に 並び、 その あいだに 別の 色で 上書きされる ことがあります。 列に 入れる ときに塗ります - 関係の
無い 人を 困った 人として扱う。 つながりの 無い 人は、 どちらの 組でもかまいません。 falseにする 理由には なりません
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「塗られていない
人を 1 人選んで 0 で 塗り、 幅優先で たどって、 隣を 反対の 色で 塗ります。 隣が すでに 自分と 同じ 色なら false です。 グラフが 離れているかもしれないので、 塗られていない 人が 残っていれば、 その 人から また 始めます。 どの 人も 関係も 数回しか 見ないので、 時間は O(V + E) です。」
書き終えたら、
「例 2 で、
0 を 0 で 塗り、 隣の 1 と 2 を 1 で 塗ります。 次に 1 を 取り出すと、 隣の 2 は もう 1 で 塗られていて、 1 と 同じ色です。 同じ 組に なってしまうので false を 返します。」