I have a directed graph, that looks like this:
I want to find the cheapest path from Start to End where the orange dotted lines are all required for the path to be valid.
The natural shortest path would be: Start -> A -> B -> End with the resultant cost = 5, but we have not met all required edge visits.
The path I want to find (via a general solution) is Start -> A -> B -> C -> D -> B -> End where the cost = 7 and we have met all required edge visits.
Does anyone have any thoughts on how to require such edge traversals?
Let R be the set of required edges and F = |R|. Let G be the input graph, t (resp. s) the starting (resp. ending) point of the requested path.
The first step is to create another graph. This graph will have exactly F+2 vertices:
To create this graph, you will have to do the following:
The complexity to build this graph is O((F+2)².(E+V).log(V)) where E (resp. V) is the number of edges (resp. vertices) in the original graph.
From this point, we have to find the shortest Hamiltonian Path in the newly created graph. Unfortunately, this task is a hard problem. We have no better way than exploring every possible path. But that doesn't mean we can't do it cleverly.
We will perform the search using backtracking. We can achieve this by maintaining two sets:
Before digging in the algorithm definition, here are the main ideas. We cannot do anything else than exploring the whole space of possible paths in the new graph. At each step, we have to make a decision: which edge do we take next? This leads to a sequence of decisions until we cannot move anymore or we reached s. But now we need to go back and cancel decisions to see if we can do better by changing a direction. To cancel decisions we proceed like this:
The final algorithm can be summed up in this fashion: (I give an iterative implementation, one can find a recursive implementation a tad easier and clearer)
Let K
← []
, L[0..R+1]
← []
and U
← V (where V is the set of every vertex in the working graph minus the starting and ending vertices t and s). Finally let l
← i
← 0 and best_path_length
← ∞ and best_path
← []
While (i
≥ 0):
U
≠ []
c
← U.popFront()
(we take the head of U)L[i].pushBack(c)
i == R+1
AND (l
== weight(cur_path.back()
, s) + l
) < best_path_length
:
best_path_length
← l
cur_path
K.tail()
and c
, and weight(e)
+ l
< best_path_length
: (if K
is empty, then replace K.tail()
with t in the previous statement)
K.pushBack(c)
i
← i
+1l
← weight(e)
+ l
cur_path.pushBack(c)
L[i]
at the end of U
L[i]
← []
i
← i
-1cur_path.popBack()
At the end of the while loop (while (i ≥ 0)
), best_path
will hold the best path (in the new graph). From there you just have to get the edges' payload to rebuild the path in the original graph.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With