Processing math: 100%

単一始点最短路(Dijkstra法)

重みが全て0以上である任意の重み付きグラフGにおける単一始点最短路は,Dijkstra法によってO(|E|log|V|)で求めることができる.

単一始点最短路とは,あるノードsGから,Gに含まれる任意のノードまでの最短路である.以降始点をsと呼ぶ.

説明

以下では,あるノードiについて,sからiまでの最短路長をdsiとする.また,あるノードiから出てあるノードjに入る(有向)辺の重みをeijとする.

前提条件から全ての辺の重みが0以上であるため,sから到達可能な任意のyについて以下の式が成り立つ.

dsy=min(dsx+exy)dsxdsy

よって,dsyを小さい方から求めていくことでsを始点とする単一始点最短路を求めることができる.

単一終点最短路

Gに含まれる辺の向きを全て逆向きにしたグラフをˆGとし,iGに対応するˆGのノードをˆiと表す.

任意のノードについてsを終点とする最短路を求めるには,ˆGについてˆsを始点とした最短路を求めればよい.

説明

以下では,あるノードiについて,iからsまでの全経路の集合をPisと表す.

任意のノードxGについて,Pxsの辺の向きを全て逆向きにした経路の集合はPˆsˆxと明らかに等しいため,sを終点とする最短路を求めることはˆsを始点とする最短路を求めることと同義である.

実装

プライオリティキューを用いて,路長優先探索(造語)をすることで求められる.計算量は,取り出しにO(log|V|)かかるキューを用いていることから,O(|E|log|V|)となる.

コード

pair<vector<i64>, vector<int>> dijkstra(int s, i64 n, Graph &g) { vector<int> prev(n, -1); vector<i64> d(n, inf); d[s] = 0; priority_queue<pair<i64, int>, vector<pair<i64, int>>, greater<pair<i64, int>>> que; que.push({0, s}); while (!que.empty()) { pair<i64, i64> p = que.top();que.pop(); int v = p.second; if (d[v] < p.first) continue; for (Edge &e: g[v]) { if (d[e.to] > d[v] + e.cost) { d[e.to] = d[v] + e.cost; prev[e.to] = v; que.push({d[e.to], e.to}); } } } return {d, prev}; } // 経路復元 vector<pair<int, int>> get_path(int t, vector<int> &prev) { vector<pair<int, int>> path; for (; prev[t] != -1; t = prev[t]) path.push_back({prev[t], t}); reverse(all(path)); return move(path); }