最短経路問題

単一始点

ある頂点からの最短距離を求めたい場合.負の辺がないとする.

未確定頂点のうち,最短距離のものは確定できる(他の頂点からの更新で小さくなりえない).つまり,最短距離の未確定頂点の集合を取り出せるデータ構造があれば解ける.

BFS

最も簡単な例は迷路(隣接頂点との距離が1).

スタート地点をキューに入れて,キューが空になるまで(あるいはゴールに着くまで)操作を続ける.

訪れた頂点の隣接頂点をキューにpushする.こうすると,キューの下から上にスタートから距離が短い順に並ぶ.よって,キューからpopした頂点は,その時点でスタート地点からの距離が確定する.

すでに確定した頂点をキューにpushしないように,見たことをフラグ管理するか,距離を記録する配列をINFで初期化して,INFより小さい値が格納されている頂点はcontinueする.

キューが空になってもフラグがFalseのままか,距離がINFのままであればスタート地点から到達できない.

全ての頂点を訪れる必要がない場合,訪れた頂点を配列で管理するとTLEする場合がある.この場合は,訪れた頂点をsetで管理すると改善する(例:ABC254E - Small d and k - 競プロはじめました).

ダイクストラ法

隣接頂点との距離が様々な場合,上と同じようにキューから取り出した頂点の隣接頂点を順にキューに入れていっても,最短距離の頂点を取り出すことができない(たくさん頂点をたどって遠回りしたほうが距離としては短くなる可能性があるため).

そこで,

  • 訪れた頂点の(隣接頂点,距離)を順に入れていくが
  • 距離が小さい順に取り出すことができる
データ構造がほしい.

これは,優先度付きキュー(priority queue),ヒープ (heqp) を使えば実現できる.

ただし,優先度付きキューには早くたどり着いた頂点から情報を入れていく一方で,取り出すのは距離が短い順なので,古い情報が残っていることに注意しなくてはならない.古い情報(これは,「配列に記録されている距離 < 優先度付きキューに記録されている距離」で判定できる)に出くわしたら,その後の処理をスキップする.

01BFS

辺の長さが2種類(0, 1とする)だけの場合,両端キュー (deque) を使って最短距離の未確定頂点を取り出す方法がある.

これは,

  • コスト0で行ける隣接頂点は前から両端キューに入れて
  • コスト1で行ける隣接頂点は後ろから両端キューに入れる
とすればよい.

ダイクストラ法と同じく,両端キューには古い情報が含まれ得る点に注意する.