幅優先探索 (BFS) の色塗りで、つながった 2 人を別の組に分けられるか調べる

グラフ問題 115

人と人のつながりを、どのつながりも 2 つの組をまたぐように 2 組へ分けられるか (二部グラフか) を調べます。隣を反対の色で塗りながらたどり、同じ色どうしがつながっていたら分けられない、と判断する練習です。

海外で定番の問題リスト 4 本には入っていません

問題

運動会の綱引きで、参加者を赤と白の 2 組に分けます。参加者には 0 からの番号が付いていて、adj[i] は、i 番の人と同じ組にしてはいけない人 (前の試合でもめた相手) の番号の並びです。この関係は両向きで、i 番の並びに j があれば、j 番の並びにも i があります。

どの 2 人も、同じ組にしてはいけない相手と別の組になるように、全員を 2 組に分けられるなら true、分けられないなら false を返す関数を書いてください。

  • 片方の組が 0 人になってもかまいません
  • 関係が 1 つも無い人は、どちらの組に入れてもかまいません
  • 参加者の数は 1 以上 100 以下です。自分自身との関係や、同じ 2 人の 2 重の関係はありません

入力と出力の例

例 1

0 と 3 を赤、1 と 2 を白にすると、どの関係も赤と白をまたぎます。

adj = [[1, 2], [0, 3], [0, 3], [1, 2]]
true

例 2

0、1、2 の 3 人が互いにもめています。3 人のうち 2 人は、必ず同じ組になります。

adj = [[1, 2], [0, 2], [0, 1]]
false

例 3

0 と 1 の 2 人と、関係の無い 2 番がいます。離れた人も、どちらかの組に入れれば分けられます。

adj = [[1], [0], []]
true

面接での聞かれ方

面接では、短く次のように渡されることが多いです。

  • 「無向グラフが二部グラフかどうかを判定してください」
  • 書き終えると「グラフがいくつかに離れていたら?」と、全部のまとまりを調べたかを聞かれます
  • 「なぜ 2 色で塗れないことと、奇数の長さの輪があることが同じなのですか?」と、理由を聞かれることがあります

グラフが 1 つにつながっているとは限らないことを最初に確かめておくと、始める節を 1 つにしてしまう間違いを防げます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、この型の要点を表とコードでまとめています。

グラフ探索は、つながりを近い順か奥からたどります。迷路、最短の手数、講座を受ける順のような問題が同じ形です。

main.py
def two_teams(adj: list[list[int]]) -> bool:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(two_teams([[1, 2], [0, 3], [0, 3], [1, 2]]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します