幅優先探索 (BFS) アルゴリズム

あるグラフに n 個の頂点と e 個の辺が与えられたときに、任意の頂点から始めて、その頂点から到達可能な頂点を段階的にすべて訪問したいと考えてみましょう。実現方法はいろいろありますが、その中でも特によく使われるのが幅優先探索 (BFS) アルゴリズムです。BFS の特徴として、始点から各頂点への最短経路が簡単に求められることが挙げられ、さまざまな応用分野で重宝されます。
Video preview
BFS アルゴリズムをイメージするのに「グラフを燃やして、火が伝わっていく様子を観察する」という比喩がよく使われます。各頂点は 1 分間燃え続け、その後火は隣接する頂点に伝わります。伝わった頂点も 1 分燃えて、さらに次の隣接頂点に火が伝わる、といった具合です。
まず始点の頂点に火をつけると、次にその頂点の近隣が燃え始め、1 分後にはさらにその近隣の頂点へ火が移り…というサイクルを繰り返すと、やがて燃え尽きていない頂点がなくなります。この火が広がるイメージを頭に置くと、BFS の実装がより理解しやすくなるでしょう。
BFS の実装例として、queue を使いながら、どの頂点を “使用済み(訪問済み)” にしたかを記録するリストを用意して進める方法を見てみます。
from collections import deque

n, e = map(int, input().split())        # n: 頂点数, e: 辺の数
g = [[] for _ in range(n)]              # グラフ - 隣接リスト

for _ in range(e):                      # 辺を読み込む
    a, b = map(int, input().split())    # a -> b および b -> a
    g[a].append(b)
    g[b].append(a)

start = int(input())                    # 開始頂点
used = [False] * n                      # 使用済み(訪問済み)の頂点
q = deque([start])                      # 頂点のキュー
used[start] = True                      # 開始頂点を訪問済みにする

while q:                                # キューが空でない間
    v = q.popleft()                     # キューから頂点を取り出す
    print(v, end=' ')                   # 頂点を出力する
    for to in g[v]:                     # v とつながっている各頂点について
        if not used[to]:                # もし未訪問の頂点なら
            q.append(to)                # 頂点をキューに追加する
            used[to] = True             # 頂点を訪問済みにする
まず、グラフの初期化をして start 頂点から探索を開始します。最初は used のすべてを False としておきますが、訪問する頂点が決まったら、キューに追加して used を更新していきます。これが「グラフが燃える」イメージに対応しています。各ステップでは、現在の頂点 v に隣接していて、まだ訪問していない頂点をすべてキューに入れ、訪問済みに設定する、という操作を繰り返します。
以下の入力例でアルゴリズムをシミュレーションしてみましょう。
n = 4, e = 4
The input a, b pairs are the following: [(0, 1), (0, 2), (1, 2), (3, 2)]
The starting vertex is 3
  1. used = [False, False, False, True], q = [3]
  1. v = 3, q = [] → add all the neighbors that are not used to the queue
  1. used = [False, False, True, True], q = [2]
  1. v = 2, q = [] → add all the neighbors that are not used to the queue
  1. used = [True, True, True, True], q = [0, 1]
  1. v = 0, q = [1] → add all the neighbors that are not used to the queue
  1. v = 1, q = [] → add all the neighbors that are not used to the queue
  1. Done
n = 5, e = 4
The input a, b pairs are the following: [(0, 1), (0, 2), (1, 2), (3, 4)]
The starting vertex is 0
  1. used = [True, False, False, False, False], q = [0]
  1. v = 0, q = [] → add all the neighbors that are not used to the queue
  1. used = [True, True, True, False, False], q = [1, 2]
  1. v = 1, q = [2] → add all the neighbors that are not used to the queue
  1. v = 2, q = [] → add all the neighbors that are not used to the queue
  1. Done
    1. Notice how nodes 3 and 4 were never visited.
      They were not connected to the initial node 0.

チャレンジ: グラフにおける連結成分数を数える

頂点が v 個、辺が e 個ある無向グラフが与えられたとき、連結成分の数を求める問題です。ある頂点群同士が「お互いに行き来可能」な場合、それらの頂点たちは同じ連結成分に属するといえます。

入力

最初の行には、2 つの整数 v (1 ≤ v ≤ 100 000) と e (1 ≤ e ≤ 100 000) が与えられます。
続く e 行には、それぞれ v1, v2 (1 ≤ v1, v2 ≤ v) が与えられ、頂点 v1 と頂点 v2 が相互に接続していることを表します。

出力

このグラフに含まれる連結成分の数を出力するプログラムを作成してください。

入力
出力
7 6 1 2 2 3 1 3 4 5 5 6 4 6
3
2 1 1 2
1
2 0
2
 

Constraints

Time limit: 3 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue