素集合 (Union-⁠Find) でつながった町のまとまりを数える

町どうしが​道で​つながっているかを​表す​表から、​行き来できる​町の​まと​まりが​いくつあるかを​数える。​町を​ 1 つずつ​同じまとまり​へ合わせていく、​素集合の​練習です。

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

問題

n 個の町があり、0 から n - 1 の番号が付いています。2 次元の並び links を受け取ります。links[i][j] が 1 なら町 i と町 j は道で直接つながっていて、0 ならつながっていません。道を何本かたどって行き来できる町どうしは、同じまとまりに入ります。まとまりがいくつあるかを返す関数を書いてください。

  • links は n 行 n 列で、値は数の 0 か 1 です
  • links[i][i] はいつも 1 です。links[i][j] と links[j][i] はいつも同じです
  • n は 1 以上 200 以下です

入力と出力の例

例 1

町 0 と町 1、町 2 と町 3 が、それぞれ道でつながっています。

入力

links = [[1, 1, 0, 0],
  [1, 1, 0, 0],
  [0, 0, 1, 1],
  [0, 0, 1, 1]]

出力

2

例 2

道が 1 本もありません。どの町も 1 つで 1 つのまとまりです。

入力

links = [[1, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 1]]

出力

4

例 3

町 0 と町 1 は直接はつながっていませんが、どちらも町 3 とつながっているので、同じまとまりです。町 2 と町 4 は 1 つずつです。

入力

links = [[1, 0, 0, 1, 0],
  [0, 1, 0, 1, 0],
  [0, 0, 1, 0, 0],
  [1, 1, 0, 1, 0],
  [0, 0, 0, 0, 1]]

出力

3

面接での聞かれ方

面接では、友だちのつながりや、機械どうしの回線などの場面で渡されることが多いです。

  • 「つながりの表から、グループがいくつあるかを数えてください」
  • 書き終えたあとに「深さ優先でも解けますが、素集合で書くと何がうれしいですか?」「道があとから 1 本ずつ増えていくなら、どう数え直しますか?」と続けて聞かれます
  • 「find を速くするための工夫を 2 つ挙げてください」と、素集合の中身を聞かれることもあります

表が対角線をはさんで同じであること、自分自身との値が 1 であることを、書く前に確かめておくと、見る範囲を半分にできます。

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

main.py
def count_groups(links):
    # ここに書きます
    pass


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

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