Pattern #60

Floyd-Warshall Algorithm

All-pairs shortest paths, intermediate-node matrix DP, correct loop order, negative cycles, path reconstruction, closure variants, and Go templates.

Must Solve

10 core questions — solve these first.

  1. 1.Floyd-Warshall All-Pairs Shortest Path
    medium
  2. 2.Find the City With the Smallest Number of Neighbors
    medium
  3. 3.Shortest Distance Between Every Pair of Nodes
    medium
  4. 4.Detect Negative Cycle Using Floyd-Warshall
    medium
  5. 5.Transitive Closure / Reachability
    medium
  6. 6.Minimum Cost Between All Pairs
    medium
  7. 7.Shortest Path in Weighted Matrix Graph
    medium
  8. 8.All-Pairs Shortest Path with Negative Edges
    hard
  9. 9.Reconstruct Shortest Paths Between All Pairs
    hard
  10. 10.Find Minimum Cycle Cost — Related Variant
    hard

Also Important

8 closure and advanced variants.

  1. 11.Evaluate Division — Closure Thinking
    medium
  2. 12.Course Prerequisite Reachability
    medium
  3. 13.Network Reachability Between Every Pair
    medium
  4. 14.Minimum Distance Queries
    medium
  5. 15.Boolean Floyd-Warshall / Warshall Algorithm
    medium
  6. 16.Minimax Path Variants
    hard
  7. 17.Maximum Product / Probability Closure
    hard
  8. 18.Johnson's Algorithm — Advanced Comparison
    hard

How to Think

Need distance between every pair?Floyd-Warshall
Graph small enough for O(V³)?Matrix DP
Negative edges?Allowed, but inspect negative cycles
Many queries after preprocessing?Answer dist[u][v] in O(1)
For every intermediate k:
  For every source i:
    For every target j:
      try route i → k → j

Go Quick Reference — Matrix DP

func floydWarshall(dist [][]int64) [][]int64 {
    n := len(dist)
    // Before calling: diagonal = 0 and missing edges = INF.
    for k := 0; k < n; k++ { // k MUST be outermost
        for i := 0; i < n; i++ {
            if dist[i][k] == INF { continue }
            for j := 0; j < n; j++ {
                if dist[k][j] == INF { continue }
                candidate := dist[i][k] + dist[k][j]
                if candidate < dist[i][j] {
                    dist[i][j] = candidate
                }
            }
        }
    }
    return dist
}

After processing k, dist[i][j] is the best path whose intermediate nodes come only from the processed set.

Variants & Path Reconstruction

Negative cycle

After completion, dist[v][v] < 0 identifies a node on a negative cycle.

Actual path

Maintain next[i][j]; when i→k→j improves, set next[i][j] = next[i][k].

Transitive closure

Use reachable[i][j] = reachable[i][j] || (reachable[i][k] && reachable[k][j]).

Minimax path

Use min(current, max(leftPart, rightPart)) instead of the usual min-plus formula.

Multiple edges

Initialize dist[u][v] with the minimum among parallel edge weights.

Directed graph

Set only dist[u][v]; for undirected graphs also set dist[v][u].

Visual Memory Rule
Matrix [i][j] → best i-to-j cost
k → newly allowed middle node
Update → min(direct, i→k + k→j)
Loop order → k, i, j
Negative cycle → negative diagonal

Complexity

Time:          O(V³)
Distance space: O(V²)
Path matrix:    O(V²)
Query afterward: O(1)

Excellent for modest dense graphs or many pair queries; avoid it when V is huge and the graph is sparse.

Common Interview Mistakes

• Using the wrong loop order—k must be outermost

• Forgetting dist[i][i] = 0

• Adding INF values and overflowing

• Ignoring negative diagonal cycle detection

• Using O(V³) on an enormous sparse graph

• Overwriting a cheaper parallel edge

• Confusing all-pairs with one-source shortest path

Interview Rules

All pairs          → Floyd-Warshall
State              → dist[i][j]
Transition         → min(direct, through k)
Loop order         → k, then i, then j
Diagonal           → 0 initially
Negative cycle     → dist[v][v] < 0
Actual paths       → next matrix
Huge sparse graph  → repeated Dijkstra / Johnson

Production Thinking

Routing tables → precompute all-pairs costs when the network is modest and queries are frequent.

Service reachability → boolean closure answers whether any dependency chain exists.

Production warning → O(V³) time and O(V²) memory become impractical quickly.

Remember This

Floyd-Warshall → all pairs
State → dist[i][j]
Middle node → k
Loop order → k, i, j
Missing path → INF guard
Negative cycle → dist[v][v] < 0
Time → O(V³)

💡 Golden Rule: Let each node become an allowed intermediate, one at a time, and improve every source-target pair through it.