両側ダイクストラによる二点間最短路 (有向グラフ対応)
2019/08/12: 有向グラフ対応
概要
無向グラフの $s$-$t$ 最短路を,両側からダイクストラすることで求める. $\mu = \min_{e = vw \in E}$ { s-v 最短距離 + $e$ の重み + w-t 最短距離} が,2 つのヒープの先頭の和以上になったら $\mu$ を返す. 辺の向きを逆にしたグラフを使うと有向グラフに対応できる.
使い方
グラフ g,始点 s,終点 t, 有向グラフであるかのフラグ dir を渡すと s-t 最短距離を返す (dir <=> g が有向グラフ). 負辺はダメ.
計算量
$O((V+E) \log V)$
実装
Graph makeReversed(const Graph &g) {
int n = g.size();
Graph revg(n);
for (auto &es : g) {
for (auto &e : es) {
Edge re = e;
std::swap(re.src, re.dst);
revg[re.src].push_back(re);
}
}
return revg;
}
Weight bidirectionalDijkstraImpl(const Graph &g, const Graph &rg, int s, int t) {
int n = g.size();
using State = std::pair<Weight, int>;
std::priority_queue<State, std::vector<State>, std::greater<State>> pq[2];
pq[0].emplace(0, s);
pq[1].emplace(0, t);
constexpr Weight INF = std::numeric_limits<Weight>::max() / 8;
std::vector<Weight> dist[2];
dist[0] = dist[1] = std::vector<Weight>(n, INF);
dist[0][s] = dist[1][t] = 0;
const Graph *gs[2] = {&g, &rg};
Weight mu = INF;
while (pq[0].size() && pq[1].size()) {
if (pq[0].top().first + pq[1].top().first >= mu) break;
int k = pq[0].top().first <= pq[1].top().first ? 0 : 1;
Weight d;
int v;
std::tie(d, v) = pq[k].top();
pq[k].pop();
if (d < dist[k][v]) continue;
for (auto &e : (*gs[k])[v]) {
Weight w = e.dst;
if (dist[k][w] > dist[k][v] + e.weight) {
dist[k][w] = dist[k][v] + e.weight;
pq[k].emplace(dist[k][w], w);
mu = std::min(mu, dist[k][v] + e.weight + dist[1 - k][w]);
}
}
}
return mu;
}
Weight bidirectionalDijkstra(const Graph &g, int s, int t, bool dir) {
if (s == t) return 0;
if (dir) {
Graph revg = makeReversed(g);
return bidirectionalDijkstraImpl(g, revg, s, t);
} else {
return bidirectionalDijkstraImpl(g, g, s, t);
}
}
検証
想定解法ではないので一部 TLE だが多くで AC
- ARC012D https://abc012.contest.atcoder.jp/submissions/6858958
- GRL1A http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=3804978