単一始点最短路(Dijkstra法)
重みが全て0以上である任意の重み付きグラフGにおける単一始点最短路は,Dijkstra法によってO(|E|log|V|)で求めることができる.
単一始点最短路とは,あるノードs∈Gから,Gに含まれる任意のノードまでの最短路である.以降始点をsと呼ぶ.
説明
以下では,あるノードiについて,sからiまでの最短路長をds→iとする.また,あるノードiから出てあるノードjに入る(有向)辺の重みをei→jとする.
前提条件から全ての辺の重みが0以上であるため,sから到達可能な任意のyについて以下の式が成り立つ.
ds→y=min(ds→x+ex→y)ただしds→x≥ds→yとする.
よって,ds→yを小さい方から求めていくことでsを始点とする単一始点最短路を求めることができる.
単一終点最短路
Gに含まれる辺の向きを全て逆向きにしたグラフをˆGとし,i∈Gに対応するˆGのノードをˆiと表す.
任意のノードについてsを終点とする最短路を求めるには,ˆGについてˆsを始点とした最短路を求めればよい.
説明
以下では,あるノードiについて,iからsまでの全経路の集合をPi→sと表す.
任意のノードx∈Gについて,Px→sの辺の向きを全て逆向きにした経路の集合は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);
}