Skip to content

Commit 02236d7

Browse files
author
Takanori MAEHARA
authored
Eppstein's k shortest walks
1 parent 516f777 commit 02236d7

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

graph/k_shortest_walks.cc

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//
2+
// K Shortest Walks (Simplified Eppstein)
3+
//
4+
// Description
5+
//
6+
// We are given a weighted graph. The k-shortest walks problem
7+
// seeks k different s-t walks (paths allowing repeated vertices)
8+
// in the increasing order of the lengths.
9+
//
10+
// If we maintain each walks explicitly, it must costs O(k^2 m) time.
11+
// To avoid this complexity, we maintain the walks in a compact format.
12+
// Let us fix a reverse shortest path tree from t. A deviation is an
13+
// edge that is not on the tree. Any walk is represented by a concatenation
14+
// of deviations and paths on the tree. We enumerate all possible
15+
// deviations and use the best-first search to find the k-th solution.
16+
//
17+
// The Eppstein's algorithm maintains the set of deviations by
18+
// the augmented persistent heaps and emurates the best-first search.
19+
// Here, we implemented a simplified version of the Eppstein's algorithm,
20+
// which uses the simple persistent heaps instead of the augmented ones.
21+
// It increases the space from O(m + n log n) to O(m log n).
22+
//
23+
// Complexity:
24+
//
25+
// O(m log m) construction
26+
// O(k log k) for k-th search
27+
//
28+
// Verified:
29+
//
30+
// UTPC2013_10 J K-th Cycle
31+
//
32+
// References:
33+
//
34+
// David Eppstein (1998):
35+
// "Finding the k shortest paths",
36+
// SIAM Journal on computing, vol.28, no.2, pp.652--673.
37+
//
38+
#include <bits/stdc++.h>
39+
40+
using namespace std;
41+
42+
#define fst first
43+
#define snd second
44+
#define all(c) ((c).begin()), ((c).end())
45+
#define TEST(s) if (!(s)) { cout << __LINE__ << " " << #s << endl; exit(-1); }
46+
47+
struct Graph {
48+
int n, m = 0;
49+
vector<int> head; // Vertex
50+
vector<int> src, dst, next, prev; // Edge
51+
52+
using Weight = long long;
53+
vector<Weight> weight;
54+
Graph(int n) : n(n), head(n, -1) { }
55+
int addEdge(int u, int v, Weight w) {
56+
next.push_back(head[u]);
57+
src.push_back(u);
58+
dst.push_back(v);
59+
weight.push_back(w);
60+
return head[u] = m++;
61+
}
62+
};
63+
constexpr Graph::Weight INF = 1e15;
64+
65+
struct KShortestWalks {
66+
Graph g;
67+
vector<Graph::Weight> dist;
68+
vector<int> tree, order;
69+
void reverseDijkstra(int t) {
70+
vector<vector<int>> adj(g.n);
71+
for (int u = 0; u < g.n; ++u)
72+
for (int e = g.head[u]; e >= 0; e = g.next[e])
73+
adj[g.dst[e]].push_back(e);
74+
dist.assign(g.n, INF);
75+
tree.assign(g.n, ~g.m);
76+
using Node = tuple<Graph::Weight,int>;
77+
priority_queue<Node, vector<Node>, greater<Node>> que;
78+
que.push(make_tuple(0, t));
79+
dist[t] = 0;
80+
while (!que.empty()) {
81+
int u = get<1>(que.top()); que.pop();
82+
if (tree[u] >= 0) continue;
83+
tree[u] = ~tree[u];
84+
order.push_back(u);
85+
for (int e: adj[u]) {
86+
int v = g.src[e];
87+
if (dist[v] > dist[u] + g.weight[e]) {
88+
tree[v] = ~e;
89+
dist[v] = dist[u] + g.weight[e];
90+
que.push(Node(dist[v], v));
91+
}
92+
}
93+
}
94+
}
95+
struct Node { // Persistent Heap (Leftist Heap)
96+
int e;
97+
Graph::Weight delta;
98+
Node *left = 0, *right = 0;
99+
int rnk = 0;
100+
} *root = 0;
101+
static Node *merge(Node *x, Node *y) {
102+
if (!x) return y;
103+
if (!y) return x;
104+
if (x->delta > y->delta) swap(x, y);
105+
x = new Node(*x);
106+
x->right = merge(x->right, y);
107+
if (!x->left || x->left->rnk < x->rnk) swap(x->left, x->right);
108+
x->rnk = (x->right ? x->right->rnk : 0) + 1;
109+
return x;
110+
}
111+
vector<Node*> deviation;
112+
void buildHeap() {
113+
deviation.resize(g.n);
114+
for (int u: order) {
115+
int v = -1;
116+
for (int e = g.head[u]; e >= 0; e = g.next[e]) {
117+
if (tree[u] == e) v = g.dst[e];
118+
else if (dist[g.dst[e]] < INF) {
119+
auto delta = g.weight[e] - dist[g.src[e]] + dist[g.dst[e]];
120+
deviation[u] = merge(deviation[u], new Node({e, delta}));
121+
}
122+
}
123+
if (v >= 0) deviation[u] = merge(deviation[u], deviation[v]);
124+
}
125+
}
126+
KShortestPaths(Graph g_, int t) : g(g_) {
127+
reverseDijkstra(t);
128+
buildHeap();
129+
}
130+
void enumerate(int s, int kth) {
131+
int k = 0;
132+
Node *x = deviation[s];
133+
Graph::Weight len = dist[s];
134+
++k;
135+
using SearchNode = tuple<Node*, Graph::Weight>;
136+
auto comp = [](SearchNode x, SearchNode y) { return get<1>(x) > get<1>(y); };
137+
priority_queue<SearchNode, vector<SearchNode>, decltype(comp)> que(comp);
138+
if (x) que.push(SearchNode(x, len + x->delta));
139+
while (!que.empty() && k < kth) {
140+
tie(x, len) = que.top(); que.pop();
141+
int e = x->e, u = g.src[e], v = g.dst[e];
142+
cout << len << endl; ++k;
143+
if (deviation[v]) que.push(SearchNode(deviation[v], len+deviation[v]->delta));
144+
for (Node *y: {x->left, x->right})
145+
if (y) que.push(SearchNode(y, len + y->delta-x->delta));
146+
}
147+
while (k < kth) { cout << -1 << endl; ++k; }
148+
}
149+
};
150+
151+
void KSH_test() {
152+
int n = 4;
153+
Graph g(n);
154+
g.addEdge(0, 1, 2);
155+
g.addEdge(0, 2, 2);
156+
g.addEdge(1, 3, 4);
157+
g.addEdge(2, 3, 2);
158+
g.addEdge(1, 2, 1);
159+
g.addEdge(2, 1, 1);
160+
KShortestPaths ksh(g, 3);
161+
ksh.enumerate(0, 10);
162+
}
163+
164+
void UTPC2013_10() {
165+
int n, m, k;
166+
scanf("%d %d %d", &n, &m, &k);
167+
Graph g(n);
168+
for (int i = 0; i < m; ++i) {
169+
int u, v;
170+
long long w;
171+
scanf("%d %d %lld", &u, &v, &w);
172+
g.addEdge(u, v, w);
173+
}
174+
KShortestPaths ksh(g, 0);
175+
ksh.enumerate(0, k+1);
176+
}
177+
178+
int main() {
179+
UTPC2013_10();
180+
//KSH_test();
181+
}

0 commit comments

Comments
 (0)