3.2.2 — кратчайшие пути между всеми парами вершин сразу3.2.2 — shortest paths between every pair of vertices at once

All-Pairs Shortest Path

Иногда нужен не один кратчайший путь от старта, а полная матрица расстояний между всеми парами вершин. Запускать Дейкстру V раз — рабочий вариант, но для плотных графов есть более простой в реализации Флойд-Уоршелл, а для разреженных графов с отрицательными весами — более быстрый алгоритм Джонсона, комбинирующий Беллмана-Форда и Дейкстру из 3.2.1.Sometimes what's needed isn't one shortest path from a source, but the full distance matrix between every pair of vertices. Running Dijkstra V times works, but for dense graphs there's a simpler-to-implement Floyd-Warshall, and for sparse graphs with negative weights, a faster Johnson's algorithm combining Bellman-Ford and Dijkstra from 3.2.1.

Алгоритм Флойда-УоршеллаFloyd-Warshall algorithm

Идея — динамическое программирование по "промежуточным вершинам": dist[k][i][j] — кратчайшее расстояние от i до j, используя в качестве промежуточных только вершины из {0, ..., k}. Переход: либо путь не использует вершину k вообще (dist[k-1][i][j]), либо использует (dist[k-1][i][k] + dist[k-1][k][j]) — берём минимум. Третье измерение k можно "сжать", обновляя матрицу на месте, потому что на шаге k используются только значения с меньшим или равным k, уже вычисленные корректно.The idea is dynamic programming over "intermediate vertices": dist[k][i][j] is the shortest distance from i to j using only vertices from {0, ..., k} as intermediates. The transition: either the path doesn't use vertex k at all (dist[k-1][i][j]), or it does (dist[k-1][i][k] + dist[k-1][k][j]) — take the minimum. The third dimension k can be "collapsed," updating the matrix in place, because step k only uses values with index ≤ k, already computed correctly.

i k j dist[i][k] dist[k][j] dist[i][j] dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

разрешить ли пути через k — единственное решение на каждом шаге тройного циклаwhether to route through k — the single decision at each step of the triple loop

// dist[i][j] изначально: 0 если i==j, вес ребра если есть, INF иначе// dist[i][j] initially: 0 if i==j, edge weight if present, INF otherwise
for (int k = 0; k < n; ++k)
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            if (dist[i][k] < INF && dist[k][j] < INF)
                dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);

Сложность O(V³) не зависит от числа рёбер — это одновременно и сила (тривиальная реализация, естественная работа с отрицательными весами без отрицательных циклов), и слабость (на разреженном графе с миллионом вершин V³ невозможно посчитать, тогда как V запусков Дейкстры дают O(VE log V), кардинально меньше при малом E).The O(V³) complexity doesn't depend on the number of edges — that's both a strength (trivial to implement, naturally handles negative weights with no negative cycles) and a weakness (on a sparse graph with a million vertices, V³ is infeasible to compute, whereas V runs of Dijkstra give O(VE log V), dramatically smaller when E is small).

Алгоритм ДжонсонаJohnson's algorithm

Для разреженного графа с отрицательными весами: запускать Дейкстру V раз напрямую нельзя (отрицательные веса ломают Дейкстру, см. 3.2.1), а Флойд-Уоршелл — избыточно медленный при малом E. Джонсон решает это перевзвешиванием (reweighting): добавляет фиктивную вершину q с рёбрами веса 0 во все остальные, запускает от неё Беллмана-Форда один раз, получая потенциалы h(v) = dist(q, v), и пересчитывает вес каждого ребра как w'(u,v) = w(u,v) + h(u) - h(v). Математический факт, на котором это держится: новые веса всегда неотрицательны, а относительный порядок путей (какой из них короче) не меняется — потому что h(u) - h(v) телескопически сокращается на любом пути от u до v, оставляя лишь h(старт) - h(конец), одинаковую добавку для всех путей между одной парой вершин.For a sparse graph with negative weights: running Dijkstra V times directly is impossible (negative weights break Dijkstra, see 3.2.1), while Floyd-Warshall is needlessly slow when E is small. Johnson's algorithm fixes this via reweighting: it adds a dummy vertex q with zero-weight edges to every other vertex, runs Bellman-Ford from it once to get potentials h(v) = dist(q, v), and recomputes every edge's weight as w'(u,v) = w(u,v) + h(u) - h(v). The mathematical fact this rests on: the new weights are always non-negative, and the relative order of paths (which one is shorter) doesn't change — because h(u) - h(v) telescopes away along any path from u to v, leaving only h(start) - h(end), the same addition for every path between one pair of vertices.

После перевзвешивания граф безопасен для Дейкстры — запускаем её V раз (по разу от каждой вершины) на новых весах, затем "отменяем" перевзвешивание в полученных расстояниях. Итоговая сложность O(VE log V + VE) — для разреженного графа (E ≈ V) это O(V² log V), драматически быстрее O(V³) Флойда-Уоршелла на больших V.After reweighting, the graph is safe for Dijkstra — run it V times (once per vertex) on the new weights, then "undo" the reweighting in the resulting distances. The final complexity is O(VE log V + VE) — for a sparse graph (E ≈ V), that's O(V² log V), dramatically faster than Floyd-Warshall's O(V³) for large V.

5 примеров из реальной жизни5 real-world examples

01

Флойд-Уоршелл — таблицы расстояний между городами (карты, логистика)Floyd-Warshall — inter-city distance tables (maps, logistics)

Атлас или логистическая система с фиксированным небольшим числом узлов (сотни городов, а не миллионы перекрёстков) — идеальный случай для предвычисления полной матрицы расстояний один раз офлайн и последующих O(1) запросов "расстояние от A до B" в рантайме.An atlas or a logistics system with a small fixed number of nodes (hundreds of cities, not millions of intersections) — an ideal case for precomputing the full distance matrix once offline, then O(1) "distance from A to B" lookups at runtime.

02

Флойд-Уоршелл — вычисление транзитивного замыкания графаFloyd-Warshall — computing a graph's transitive closure

Замена операции "сложение весов, минимум" на "логическое И, логическое ИЛИ" превращает Флойда-Уоршелла в алгоритм проверки достижимости между всеми парами вершин сразу — используется в базах данных при вычислении транзитивного замыкания отношений (например, "все предки узла в иерархии").Swapping the "sum weights, take minimum" operation for "logical AND, logical OR" turns Floyd-Warshall into an algorithm checking reachability between every pair of vertices at once — used in databases when computing the transitive closure of a relation (e.g., "all ancestors of a node in a hierarchy").

03

Джонсон — сетевой анализ авиамаршрутов с субсидированными (отрицательными) участкамиJohnson's algorithm — network analysis of air routes with subsidized (negative) legs

Некоторые авиакомпании эффективно субсидируют определённые маршруты (стыковка дешевле прямого перелёта из-за программ лояльности/налоговых льгот) — граф маршрутов с такими "отрицательными" весами, разреженный (не все пары городов соединены напрямую), — ровно случай, где Джонсон выигрывает у Флойда-Уоршелла.Some airlines effectively subsidize certain routes (a connection is cheaper than a direct flight due to loyalty programs/tax breaks) — a route graph with such "negative" weights, sparse (not every city pair connects directly) — exactly the case where Johnson's algorithm beats Floyd-Warshall.

04

All-Pairs Shortest Path — вычисление центральности вершин в социальном графе (closeness/betweenness centrality)All-Pairs Shortest Path — computing vertex centrality in a social graph (closeness/betweenness centrality)

"Насколько влиятелен пользователь в сети" в некоторых метриках буквально требует знать кратчайшие пути между всеми парами пользователей (или их выборкой) — closeness centrality = обратное к сумме кратчайших расстояний от вершины до всех остальных."How influential is a user in the network" for some metrics literally requires knowing shortest paths between every pair of users (or a sample of them) — closeness centrality is the inverse of the sum of shortest distances from a vertex to all others.

05

Флойд-Уоршелл — игровой AI на небольших картах (пошаговые стратегии)Floyd-Warshall — game AI on small maps (turn-based strategy)

Пошаговая стратегия с картой из сотен клеток может позволить себе предпосчитать все попарные расстояния один раз за ход, чтобы у каждого юнита ИИ был мгновенный доступ к "кратчайший путь до любой другой клетки" без повторного запуска Дейкстры на каждое решение.A turn-based strategy game with a map of a few hundred cells can afford to precompute all pairwise distances once per turn, so every AI unit gets instant access to "shortest path to any other cell" without rerunning Dijkstra for every decision.

ГраблиGotchas

Порядок циклов k-i-j в Флойде-Уоршелле не взаимозаменяемThe k-i-j loop order in Floyd-Warshall isn't interchangeable

Внешний цикл обязан быть по k (промежуточная вершина), а не по i или j — переставить их местами нарушает инвариант динамического программирования (на шаге k должны быть уже корректны все dist, использующие вершины ≤ k) и даёт неверные результаты без какой-либо ошибки компиляции или исключения.The outer loop must be over k (the intermediate vertex), not i or j — swapping the order breaks the DP invariant (at step k, every dist using vertices ≤ k must already be correct) and produces wrong results with no compile error or exception.

Флойд-Уоршелл на графе с отрицательным циклом даёт молчаливо неверный ответFloyd-Warshall on a graph with a negative cycle silently gives a wrong answer

Как и Беллман-Форд, алгоритм требует отсутствия отрицательных циклов для корректности понятия "кратчайший путь". Признак наличия цикла — отрицательное значение на диагонали (dist[v][v] < 0) после завершения алгоритма — эту проверку часто забывают добавить.Like Bellman-Ford, the algorithm requires no negative cycles for "shortest path" to be well-defined. The tell is a negative value on the diagonal (dist[v][v] < 0) after the algorithm finishes — this check is often forgotten.

Выбор Флойда-Уоршелла "по умолчанию" без учёта разреженности графаDefaulting to Floyd-Warshall without considering graph sparsity

Флойд-Уоршелл проще для запоминания и написания на собеседовании, из-за чего его часто выбирают автоматически — но на графе с V=100 000 и разреженными связями O(V³) физически невозможно посчитать за разумное время, тогда как V запусков Дейкстры (или Джонсон при отрицательных весах) отработают на порядки быстрее.Floyd-Warshall is easier to remember and write in an interview, which makes it a common default choice — but on a graph with V=100,000 and sparse connections, O(V³) is physically infeasible to compute in reasonable time, whereas V runs of Dijkstra (or Johnson's algorithm under negative weights) finish orders of magnitude faster.