Day 093-Q1 — 01-BFS(0-1 BFS)

2026-07-16 赤色 Master / Phase 8+ ★★★★★★★★★ 01-BFS・重み0/1辺の最短路

問題

$N$ 頂点 $M$ 辺の有向グラフが与えられる。各辺の重みは $0$ または $1$ である。頂点 $1$ から各頂点への最短距離を求めよ。到達不可能な場合は -1 を出力せよ。

制約

パラメータ範囲備考
$N$$1 \le N \le 2\times10^5$頂点数
$M$$0 \le M \le 4\times10^5$辺数
$w_i$$w_i \in \{0, 1\}$辺の重み
グラフ有向$u_i \to v_i$

入出力例

入力例1

4 4
1 2 1
1 3 0
3 2 0
2 4 1

出力例1

0
0
0
1

頂点2へは直接辺(重み1)より、1→3(重み0)→2(重み0)の方が短く距離0。頂点4へは2から重み1で距離1。

概念図

重み0の辺は先頭へ、重み1の辺は末尾へ push 1 2 3 4 w=1 w=0 w=0 w=1 deque: 1 popleft → 3(前) 2(後) dist[3]=0 が先に確定 通常のBFSキューの「距離昇順」性質をO(1)操作のまま維持できる

ヒント

ヒント1(方向性)

辺の重みが $0$ か $1$ しかないグラフでは、Dijkstra 法($O(M\log N)$)を使わなくても、もっと軽い仕組みで最短路が求まる。キューの中身が常に「距離が単調非減少」になるように工夫できないか考えよ。

ヒント2(アプローチ)

dequeを使う。重み $0$ の辺で移動する場合はキューの先頭に、重み $1$ の辺で移動する場合はキューの末尾に頂点を追加する。これにより通常のBFSと同様の順序性が保たれる。

ヒント3(ほぼ答え)
from collections import deque
dist = [INF]*(n+1); dist[1] = 0
dq = deque([1])
while dq:
    u = dq.popleft()
    for v, w in graph[u]:
        nd = dist[u] + w
        if nd < dist[v]:
            dist[v] = nd
            if w == 0:
                dq.appendleft(v)
            else:
                dq.append(v)

模範解答

import sys
from collections import deque


def main():
    data = sys.stdin.buffer.read().split()
    idx = 0
    n = int(data[idx]); idx += 1
    m = int(data[idx]); idx += 1

    graph = [[] for _ in range(n + 1)]
    for _ in range(m):
        u = int(data[idx]); v = int(data[idx + 1]); w = int(data[idx + 2])
        idx += 3
        graph[u].append((v, w))

    INF = float('inf')
    dist = [INF] * (n + 1)
    dist[1] = 0
    dq = deque([1])

    while dq:
        u = dq.popleft()
        for v, w in graph[u]:
            nd = dist[u] + w
            if nd < dist[v]:
                dist[v] = nd
                if w == 0:
                    dq.appendleft(v)
                else:
                    dq.append(v)

    out = []
    for i in range(1, n + 1):
        out.append(str(dist[i]) if dist[i] != INF else "-1")
    sys.stdout.write("\n".join(out) + "\n")


main()

計算量: 各頂点・各辺は高々定数回キューに出入りするため $O(N+M)$。

Step-by-Step 解説

Step 1: 入力受け取りと隣接リスト構築

(v, w) のペアを頂点ごとのリストに格納する。

Step 2: dist配列とdequeの初期化

dist[1] = 0 とし、deque([1]) からスタートする。キューには「距離昇順に並ぶことが保証された」頂点が入る。

Step 3: メインループ(重みに応じてappendleft / append)

重み0の遷移は先頭に入れて即座に処理対象にし、重み1の遷移は末尾に回すことで、通常のBFSキューの「距離昇順」の性質を保ったまま拡張できる。

Step 4: 出力

未到達(INFのまま)の頂点には -1 を出力する。

よくあるミス

ミス原因正しい書き方
通常のBFS(appendのみ)で実装重み0の辺を考慮していない重み0はappendleft、重み1はappend
Dijkstra(heapq)で無駄に重くする01-BFSで十分と気づかないdequeでO(N+M)
古いエントリの扱いを誤解キュー保存値で判定しようとするdist[u]は常に配列参照、relaxはnd < dist[v]のみ
無向グラフと勘違いし逆辺追加問題文の見落とし本問は有向グラフなのでu→vのみ

次のステップ

  • 発展: 辺重みが $0,1,\dots,K$(小さい定数)への拡張(バケットキュー / Dial's Algorithm)
  • 発展: 「壁を最大K枚まで壊せる」問題への応用(01-BFSを層状に拡張)
  • 次回予告: 01-Trie(ビットトライ木・XOR最大値ペア探索)

自己評価

理解度: / /

自分の回答:

気づき・メモ: