3.5.12 · D3Graphs

Worked examples — Floyd-Warshall — all-pairs shortest paths, O(V³)

2,294 words10 min readBack to topic

Two symbols we will use constantly, defined now so nothing is assumed:


The scenario matrix

Every graph you feed Floyd-Warshall lands in one or more of these boxes. Below the table, each row gets its own fully worked example.

# Case class What makes it tricky Example
A All-positive edges The "easy" baseline; a detour beats a direct edge Ex 1
B A helpful negative edge Detour becomes cheaper than the direct edge (Dijkstra breaks here) Ex 2
C Unreachable pair stays forever; must not relax through it Ex 3
D Chained improvement Unlocking a later builds on a previous 's work — proves loop order Ex 4
E Negative cycle "Shortest" is undefined; diagonal goes negative Ex 5
F Tie / degenerate (self-loop, zero weight, equal routes) min picks one; self-loop must not corrupt Ex 6
G Real-world word problem Translate "cheapest flight" into a matrix Ex 7
H Exam twist: wrong loop order Same graph, broken code — see the actual wrong number Ex 8

We will use small graphs so you can check every number by hand.


Case A — all-positive edges (baseline)

Figure — Floyd-Warshall — all-pairs shortest paths, O(V³)
  1. Base matrix. , diagonal , everything else . Why this step? Floyd-Warshall must start from the direct-edge picture (, no midpoints allowed).
  2. Unlock . Nothing points into , so for and no relaxation fires. Why this step? is , never smaller.
  3. Unlock . Check vs . Relax: . Why this step? Now vertex is a legal midpoint, so route is finally on the table.
  4. Unlock . has no outgoing edges, so no path can pass through it. No change.

Verify: the only two routes are direct () and via (). . ✔


Case B — a helpful negative edge

  1. Base: .
  2. Unlock . vs . Relax: . Why this step? min does not care about the sign of an edge — it just adds and compares. A negative edge simply makes some sums smaller, which is exactly what a shortest-path algorithm wants.
  3. Other unlocks change nothing.

Verify: routes : direct , via : . . ✔


Case C — an unreachable pair

  1. Base: (no edge), and has no outgoing edge at all.
  2. Through every unlock : to relax we would need to be finite. But for every (nothing leaves ). So the sum stays . Why this step? This is the guardrail. If you use a huge finite sentinel instead of true , be careful: can overflow or look "cheaper" than another HUGE. Use a value like and either skip relaxation when either term is the sentinel, or keep the sentinel far below overflow.

Verify: has out-degree , so is unreachable stays . ✔


Case D — chained improvement (why later builds on earlier )

Figure — Floyd-Warshall — all-pairs shortest paths, O(V³)
  1. Base: , .
  2. Unlock . . Now costs using midpoint . Why this step? This half-built value () is exactly what the next unlock will reuse.
  3. Unlock . . Why this step? Here is the payoff: because step 2 already made , unlocking now reaches all the way to . A later stood on the shoulders of an earlier . This is precisely why must be the outer loop — the parent note's rule made concrete.

Verify: shortest is the 3-edge chain , beating direct . . ✔ This is Dynamic Programming in action: the answer for is assembled from the answer for .


Case E — a negative cycle

Figure — Floyd-Warshall — all-pairs shortest paths, O(V³)
  1. Base: diagonal all ; .
  2. Unlock . .
  3. Unlock . . Why this step? The diagonal entry starts at ("stand still, cost nothing"). The only way it drops below is a genuine cheaper-than-standing-still loop back to — i.e. a negative cycle.
  4. Test: scan the diagonal. negative cycle reported.

Verify: cycle weight , and it pushes to . ✔ When this happens, all shortest-path outputs on that cycle are meaningless (you can loop to ). If you must handle negative cycles cleanly across a graph, that's where Johnson's Algorithm and reweighting come in.


Case F — degenerate: self-loop, zero weight, and a tie

  1. Base: — careful! We initialize the diagonal to (standing still is free), which is , so we keep , not the self-loop weight . Why this step? The DP defines . A positive self-loop can never beat standing still, so we ignore it. (A negative self-loop, however, would be a length-1 negative cycle — caught by Case E's test.)
  2. Unlock . Check vs . Tie with current ; min keeps . No harm. Why this step? This is the exact spot the parent note called "the in-place trick": we read and while overwriting the same matrix. Because , the values we read don't shift mid-iteration. The self-loop being effectively is what makes overwriting safe.
  3. Tie handling. If two routes give equal distance, < (strict) leaves the first one in place — the answer's value is unaffected; only the reconstructed path may differ.

Verify: and (self-loop of correctly unused). ✔


Case G — real-world word problem

  1. Base matrix (rows/cols ):

    from\to A B C D
    A 0 100 250 400
    B · 0 100 -50
    C · · 0 100
    D · · · 0
  2. Unlock (=1). . Also . Why this step? The rebate leg is a negative edge — Floyd-Warshall uses it without complaint, unlike a naïve greedy pricer.

  3. Unlock (=2). vs ; not better than . No change.

  4. Result: cheapest A\to D = \50A\to B\to D$.

Verify: routes : direct ; ; ; . Cheapest . ✔ (Units: all dollars, consistent.)


Case H — exam twist: the wrong loop order

  1. What the bug does. With innermost, for a fixed it sweeps all — but it does this before the pairs it depends on have been fully computed for outer . Concretely, when it processes , the entry has not yet been improved to (that improvement lives at pair , which for this ordering may come after or without the needed chain). Why this step? The DP recurrence requires that all pairs already account for midpoint set before you unlock . Only -outermost enforces that global barrier.
  2. Trace (k innermost). Processing : it tries for . At that moment is still (never relaxed yet, since pair hasn't been improved), so . And . So stays .
  3. Buggy output: , the direct edge — wrong (correct is ).

Verify: correct order gives ; the -innermost order on this graph gives . They differ, confirming the bug is real. ✔


Recall

Recall Which case is which?

Match each symptom to its case letter. A negative diagonal entry after the run ::: Case E — negative cycle. An entry stubbornly stuck at ::: Case C — unreachable pair. Detour cheaper than the direct edge because of a negative leg ::: Case B (or G) — helpful negative edge. Answer only appears after a second unlock builds on the first ::: Case D — chained improvement, proves -outermost. Wrong answer that equals the raw direct edge ::: Case H — not outermost. A positive self-loop that must be ignored ::: Case F — degenerate; diagonal stays .