Problem Statement
The All-Pairs Shortest Paths (APSP) problem is generally defined as the following:
Given a graph
G
, what is the shortest path between all pairs of vertices inG
?
Example: Given a city (graph) with junctions (vertices) and roads (edges), what is the shortest path from every junction in the city to all the other junctions?
General Idea | \(O(V^3)\)
One way to solve this problem is to make V
calls to
a SSSP algorithm:
- V calls of \(O((V+E)logV)\) for Dijkstra’s = \(O(V^3 logV)\) if E = \(V^2\)
- V calls of \(O(VE)\) for Bellman-Ford = \(O(V^4)\) if E = \(V^2\)
An alternative \(O(V^3)\) solution to the APSP problem is the Floyd–Warshall algorithm.
The Floyd-Warshall algorithm is a Dynamic Programming algorithm that,
in essense, builds up its solution using a bottom-up approach by gradually
allowing the usage of intermediate vertices (vertices [0..k]
) to form the shortest paths.
Initially, we’ll load our graph into an Adjacency Matrix which should now represent
the direct distance between each pair of vertices, i.e there are
no intermediate vertices between any two pair of vertices (k = -1
).
Next, we’ll allow vertex 0
(k = 0
) to be used as an intermediate vertex
in the path between any two vertices. For vertices i
and j
with k = 0
,
the shortest path could be either:
- The shortest distance of the previous iterations (In this case, when
k = -1
, i.e a direct edge betweeni
andj
) - The shortest distance from
i
to 0 + the shortest distance from 0 toj
(0
is used as an intermediate vertex)
After looping over all the possible combinations of i
and j
, we’ll repeat the same
process but only allowing vertex 1
(k
is 1
) to be used as an intermediate vertex.
The shortest path between i
and j
with k = 1
could be either:
- The shorest distance computed in previous iterations (Either when
k = -1
ork = 0
) - the shortest distance from
i
to1
+ the shortest distance from1
toj
(the shortest distance as in the shortest computed distance from previous iterations)
We’ll keep on repeating this process for k = 2
, k = 3
…k = V - 1
and then,
finally, our adjacency matrix should contain all the shortest distances between any two
vertices in our graph.
A couple of things to note
- The Floyd-Warshall algorithm works well with negative edges and can even tolerate and detect negative edge cycles.
- The main benefit of using Floyd-Warshall’s algorithm is that it’s only
four to six lines long. Always consider using Floyd-Warshall when the input graph
is relatively small (
V <= 400
)
Code
C++11 code below.
Variations
SSSP on Small Weighted Graphs
Instead of using Dijskta’s/Bellman-Ford’s algorithms to solve the SSSP problem
on a relatively small graph where V <= 400
, we can use the much shorter
(coding wise) Floyd-Warshall algorithm.
Printing the Shortest Paths
Unlike in BFS/Dijkstra’s/Bellman-Ford’s algorithms, we cannot simply
use a 1D vector<int> p
to store the parent information of each vertex.
Instead, we need to use a 2D parent matrix to be able to properly reconstruct
the paths.
Transitive Closure (Warshall’s Algorithm)
The Transitive Closure problem is defined as the following:
Given a graph, determine if vertex
i
is connected to vertexj
, either directly or indirectly.
We can solve this problem using a modified FW that utilizes bitwise operations
that come with a (significant) speedboost as well. Initially, AdjMat[i][j]
will
contain 1
if vertex i
is directly connected to j
.
After running the algorithm, we can then check if i
is connected to j
either directly or indirectly by checking AdjMat[i][j]
’s value
(1
if connected, 0
if not connected).
Minimax and Maximin Problem
An excellent explanation of the maximin and minimax problem can be found in this Stack Overflow answer.
In short, the maximin path (bottleneck path) revolves around find a path from the source to the destination whose minimum-weight edge is maximized (I visualize it as trying to clamp the lower-limit upwards as much as possible).
The minimax path represents the opposite idea - the path from source to destination that minimizes the maximum edge weight (trying to clamp the upper-limit downwards as much as possible).
This problem can be solved using a slight modification of FW’s algorithm.
Instead of trying to figure out the length of the shortest path, to find the
minimax path we’ll now have to find the minimum of the max weight of all the edges
in the path from i
to j
For the maximin path, we need to find the maximum of the minimum weight of
all the edges in the path from i
to j
.
Finding the Cheapest/Negative Cycle
The Floyd-Warshall algorithm can be used to detect whether a graph has a cycle, a negative cycle and even the cheapest/smallest non-negative cycle among all possible cycles.
To do this, we simply run the stardard FW algorithm without checking
if i == j
in our loop and explicitly updating AdjMat
accordingly. We should simply
leave the case where i == j
to modified on its own in the main loop.
After the algorithm’s execution, we will find a case where AdjMat[i][i] != INF
if a cycle exists.
If a negative cycle exists, we will find a case where AdjMat[i][i] < 0
To find the cheapest non-negative cycle, we simply loop every possible
vertex i
and find the smallest AdjMat[i][i]
Finding the Diameter of a Graph
The diameter of the graph is defined as the maximum shortest path distance between any two vertices in a graph.
To find the diameter, we simply loop over AdjMat[i][j]
in \(O(V^2)\)
after FW has been executed and find the maximum value.
Finding the SCCs of a Directed Graph
Usually, to find the SCCs of a directed graph we’d use Tarjan’s O(V + E) algorithm. However, if the input graph is relatively small, we can use the much shorter FW algorithm to solve the same problem.
We’ll first run Warhshall’s algorithm (described above) in \(O(V^3)\) and then
use the following check: To find all the members of a SCC that contain vertex i
,
we’ll check for all other vertices j
If AdjMat[i][j] == 1
and AdjMat[j][i] == 1
which would indicate that bothi
and j
belong to the same SCC.