Floyd-Warshall
Dijkstra answers "how far is everything from one source?" Floyd-Warshall answers "how far is everything from everything?" Three nested loops, one DP idea, and you get all \(V^2\) pairwise distances. The algorithm is \(O(V^3)\), which is too slow for large graphs but perfect when \(V \leq 500\). Its real power emerges when you understand what each iteration actually computes — that insight unlocks a class of problems no other algorithm handles cleanly.
The DP


Define \(\text{dist}_k[i][j]\) as the shortest path from \(i\) to \(j\) using only intermediate nodes from \(\{1, 2, \ldots, k\}\).
Base case: \(\text{dist}_0[i][j]\) = direct edge weight (or \(\infty\) if no edge).
Transition: at pivot \(k\), the shortest path either uses \(k\) as an intermediate or it doesn't.
We do this in-place — no need to store all \(k\) layers:
const long long INF = 1e18;
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dist[i][k] != INF && dist[k][j] != INF)
dist[i][j] = min(dist[i][j],
dist[i][k] + dist[k][j]);
Gotcha. The loop order matters. \(k\) must be the outermost loop. If you put \(i\) or \(j\) outermost, the DP recurrence is evaluated in the wrong order and results are incorrect. This is the single most common Floyd-Warshall bug.
Why In-Place Works
You might worry that updating dist[i][k] or dist[k][j] during the \(k\)th iteration corrupts later lookups. It doesn't. When the pivot is \(k\):
dist[i][k]= shortest path from \(i\) to \(k\) using intermediates \(\{1, \ldots, k\}\). But \(k\) can't be an intermediate on a path ending at \(k\) (without a cycle), so this equals \(\text{dist}_{k-1}[i][k]\).- Same argument for
dist[k][j].
The pivot row and column are unchanged by the \(k\)th iteration. In-place is safe.
Full Trace
Graph with 4 nodes:
Edges (undirected): (1,2,3), (1,3,7), (2,4,1), (3,4,2).
Initial dist[][]:
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | 3 | 7 | ∞ |
| 2 | 3 | 0 | ∞ | 1 |
| 3 | 7 | ∞ | 0 | 2 |
| 4 | ∞ | 1 | 2 | 0 |
Pivot k=1: Route through node 1.
| Pair | Old | Via 1 | New |
|---|---|---|---|
| (2,3) | ∞ | 3+7=10 | 10 |
| (3,2) | ∞ | 7+3=10 | 10 |
Pivot k=2: Route through node 2.
| Pair | Old | Via 2 | New |
|---|---|---|---|
| (1,4) | ∞ | 3+1=4 | 4 |
| (4,1) | ∞ | 1+3=4 | 4 |
| (3,4) | 2 | 10+1=11 | 2 (no change) |
Pivot k=3: Route through node 3.
| Pair | Old | Via 3 | New |
|---|---|---|---|
| (1,4) | 4 | 7+2=9 | 4 (no change) |
No improvements.
Pivot k=4: Route through node 4.
| Pair | Old | Via 4 | New |
|---|---|---|---|
| (1,3) | 7 | 4+2=6 | 6 |
| (3,1) | 7 | 2+4=6 | 6 |
| (2,3) | 10 | 1+2=3 | 3 |
| (3,2) | 10 | 2+1=3 | 3 |
Final dist[][]:
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| 1 | 0 | 3 | 6 | 4 |
| 2 | 3 | 0 | 3 | 1 |
| 3 | 6 | 3 | 0 | 2 |
| 4 | 4 | 1 | 2 | 0 |
Verify: shortest \(1 \to 3\) is \(1 \to 2 \to 4 \to 3 = 3 + 1 + 2 = 6\). Correct.
CSES Shortest Routes II
Problem: \(N\) nodes, \(M\) edges, \(Q\) queries. Each query asks for the shortest distance between two nodes. \(N \leq 500\).
Floyd-Warshall's home turf. With \(N \leq 500\), the \(O(N^3) = 1.25 \times 10^8\) operations run comfortably. Handle multiple edges between the same pair by keeping the minimum.
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m, q;
cin >> n >> m >> q;
const long long INF = 1e18;
vector<vector<long long>> dist(n + 1,
vector<long long>(n + 1, INF));
for (int i = 1; i <= n; i++) dist[i][i] = 0;
for (int i = 0; i < m; i++) {
int a, b;
long long c;
cin >> a >> b >> c;
dist[a][b] = min(dist[a][b], c);
dist[b][a] = min(dist[b][a], c);
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dist[i][k] != INF && dist[k][j] != INF)
dist[i][j] = min(dist[i][j],
dist[i][k] + dist[k][j]);
while (q--) {
int a, b;
cin >> a >> b;
cout << (dist[a][b] >= INF ? -1 : dist[a][b]) << "\n";
}
}
Detecting Negative Cycles
After Floyd-Warshall, check the diagonal. If dist[i][i] < 0, node \(i\) lies on a negative cycle.
Key insight. Floyd-Warshall detects negative cycles for free — just check the diagonal. No extra iteration needed, unlike Bellman-Ford.
What Each Pivot k Computes
This is where Floyd-Warshall becomes more than a shortest-path algorithm. After processing pivot \(k\), dist[i][j] equals the shortest path from \(i\) to \(j\) using only nodes \(\{1, \ldots, k\}\) as intermediates. You can snapshot this state to answer "restricted" queries.
AtCoder abc208_d — Shortest Path Queries 2
Problem: Weighted directed graph, \(N\) nodes. Compute:
where \(f_k(i, j)\) is the shortest distance from \(i\) to \(j\) using only intermediates \(\{1, \ldots, k\}\), or 0 if no path exists.
This directly exposes Floyd's DP. After each pivot \(k\), sum all finite distances.
long long total = 0;
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dist[i][k] != INF && dist[k][j] != INF)
dist[i][j] = min(dist[i][j],
dist[i][k] + dist[k][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dist[i][j] != INF)
total += dist[i][j];
}
cout << total << "\n";
Key insight. Most people treat Floyd-Warshall as a black box. But each pivot produces a meaningful intermediate state. Problems that ask about "restricted intermediates" are exploiting this DP structure directly.
AtCoder abc079_d — Wall
Problem: A grid of digits 0-9. Change every non-1 digit to 1. Changing digit \(c\) to \(d\) costs cost[c][d]. Chaining is allowed: 5→3→1 might be cheaper than 5→1. Minimize total cost.
This is shortest paths on a 10-node graph where nodes are digits and edges are transformation costs. Run Floyd-Warshall on the \(10 \times 10\) matrix. Then for each cell with digit \(c \neq 1\), add dist[c][1].
// Read 10x10 cost matrix into dist[0..9][0..9]
for (int k = 0; k < 10; k++)
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
dist[i][j] = min(dist[i][j],
dist[i][k] + dist[k][j]);
// Answer: sum of dist[c][1] for each cell with digit c != 1
\(V = 10\) makes Floyd instant. This pattern — Floyd on a tiny auxiliary graph — shows up more than you'd expect.
Floyd-Warshall vs Running Dijkstra V Times
| Graph type | Floyd-Warshall | V x Dijkstra |
|---|---|---|
| Dense (\(E = V^2\)) | \(O(V^3)\) | \(O(V^3 \log V)\) |
| Sparse (\(E = V\)) | \(O(V^3)\) | \(O(V^2 \log V)\) |
| Negative edges | Works | Fails |
| Implementation | 5 lines | 15+ lines |
Rule of thumb: \(V \leq 500\) and you need all pairs? Floyd. \(V > 500\)? You probably can't store the \(V^2\) output anyway.
Exercises
-
Loop order. Swap the \(k\) and \(i\) loops. Run it on the 4-node example. Which entries end up wrong and why?
-
Path reconstruction. Track
next[i][j]— the first node after \(i\) on the shortest path to \(j\). Whendist[i][k] + dist[k][j]improvesdist[i][j], setnext[i][j] = next[i][k]. How do you reconstruct the full path? -
Transitive closure. Replace
minwith logical OR and weights with booleans. What does the algorithm compute? (Reachability between all pairs — useful for LC 1462.)
Problems
| Problem | Link | Difficulty |
|---|---|---|
| CSES — Shortest Routes II | cses.fi/problemset/task/1672 | Easy |
| AtCoder abc208_d — Shortest Path Queries 2 | atcoder.jp/contests/abc208/tasks/abc208_d | Medium |
| AtCoder abc079_d — Wall | atcoder.jp/contests/abc079/tasks/abc079_d | Easy |
| LC 1334 — Find the City | leetcode.com/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance | Medium |
| LC 1462 — Course Schedule IV | leetcode.com/problems/course-schedule-iv | Medium |