Contents
概要
ダイクストラ法は、単一始点最短経路問題のアルゴリズムの一つです。
基本的な考え方は貪欲法です。以上!
オーダーは実装方法によって異なり、挑戦数をV、辺の数をEとした時以下のようになります。
- 深さ優先探索を用いる場合:O(V^2)
- priority_queueを用いる場合:O(E logV)
DFSだとスタックオーバーフローの可能性もあるので、以下ではpriority_queueの例を掲載します。
制約
コストが負の辺(正確には負の閉路)が存在する場合、このアルゴリズムは使用できないことに注意してください。
負辺を持つグラフの最短経路問題はベルマンフォード法を使用しましょう。
経路復元
探索中に経路を更新する際、その頂点にどの頂点から来たか、一個手前の頂点の情報を保存しておきます。
ゴール側からその情報を辿ることで経路を復元することができます。
ライブラリ
拡張ダイクストラを実装しやすくするために、priority_queueをtupleで実装しています。
// const long long INF = 1LL << 60;
template <typename T>
struct Edge {
int to;
T cost;
};
// Dijkstra
template <typename T>
class Dijkstra {
private:
vector<vector<Edge<T>>> G;
int num_v;
public:
Dijkstra(int num_v) : G(num_v), num_v(num_v) {
}
void add_edge(int from, int to, T cost) {
G[from].push_back((Edge<T>){to, cost});
}
// start: Start point
// result: result
void solve(int start, vector<T>& result) {
vector<int> tmp;
solve(start, result, tmp);
}
void solve(int start, vector<T>& result, vector<int>& prev) {
result.resize(num_v);
std::fill(result.begin(), result.end(), INF);
prev.resize(num_v);
std::fill(prev.begin(), prev.end(), -1);
// get<0>(T): cost
// get<1>(T): index
// get<2>(T): ... ( for extent)
using Tup = tuple<T, int>;
priority_queue<Tup, vector<Tup>, greater<Tup>> que;
que.emplace(0, start);
result[start] = 0;
while (!que.empty()) {
Tup p = que.top();
que.pop();
T t = get<0>(p);
int v = get<1>(p);
// queueに入れてから別の最短経路が見つかった場合、探索処理は不要
if (result[v] != t) {
continue;
}
// vの各辺に対しよりコストの低い経路があるかを確認
for (auto& e : G[v]) {
if (result[e.to] > result[v] + e.cost) {
prev[e.to] = v;
result[e.to] = result[v] + e.cost;
que.emplace(result[v] + e.cost, e.to);
}
}
}
}
// t: 目的地
vector<int> get_path(const vector<int>& prev, int t) {
vector<int> path;
for (int v = t; v != -1; v = prev[v]) {
path.push_back(v);
}
reverse(path.begin(), path.end());
return path;
}
};
関連問題
- トレジャーハント (ABC035)
純粋なダイクストラです。 - Two Currencies (ABC164)
拡張ダイクストラです。