Алгоритм Форда — Беллмана
Кратчайшие пути при отрицательных весах. Обычная динамика, с которой исторически и начался сам термин.
4 мин
Дейкстру на отрицательных рёбрах запускать нельзя. Флойд их переносит, но считает все пары за . Если нужны пути из одной вершины, есть алгоритм за .
Это динамика
— длина кратчайшего пути из в , использующего не больше рёбер.
База: , остальное — бесконечность.
Переход: путь не больше чем из ребра — это либо путь не больше чем из рёбер, либо такой путь плюс одно ребро.
Ответ — . Почему хватает: в кратчайшем пути нет циклов. Цикл неотрицательного веса можно выбросить, не ухудшив; цикл отрицательного веса означает, что кратчайшего пути нет вовсе. А путь без повторов содержит не больше ребра.
Авторы алгоритма описали его именно как «динамическое программирование» — с этой работы термин и пошёл.
Реализация
Первую размерность, как обычно в динамике, можно не хранить:
vector<long long> d(n, INF);
d[s] = 0;
for (int it = 0; it < n; it++) {
bool any = false;
for (auto& [u, v, w] : edges)
if (d[u] < INF && d[u] + w < d[v]) { d[v] = d[u] + w; any = true; }
if (!any) break; // расстояния перестали меняться
}
Проверено: на 40 000 случайных графов расстояния совпали с перебором всех простых путей.
Проверка d[u] < INF обязательна: без неё бесконечность плюс отрицательный вес даст «улучшение» недостижимой вершины.
Отрицательные циклы
Если после итерации расстояние всё ещё улучшается, в графе есть отрицательный цикл, достижимый из .
bool negativeCycle = false;
for (auto& [u, v, w] : edges)
if (d[u] < INF && d[u] + w < d[v]) negativeCycle = true;
Чтобы найти сам цикл, запомните вершину, где произошло улучшение, поднимитесь от неё по массиву предков раз (гарантированно попадёте на цикл) и пройдите по нему до повтора.
Ранний выход и порядок рёбер
Строка if (!any) break — не косметика. Число итераций сильно зависит от того, в каком порядке лежат рёбра в списке.
Измерено на цепочке:
| граф | итераций | |
|---|---|---|
| цепочка, рёбра по порядку | 1 000 | 2 |
| цепочка, рёбра задом наперёд | 1 000 | 1 000 |
| цепочка, рёбра по порядку | 10 000 | 2 |
| цепочка, рёбра задом наперёд | 10 000 | 10 000 |
Разница в 5000 раз, и алгоритм тот же. Если рёбра случайно оказались в удобном порядке, расстояния доходят до конца за один проход; если в неудобном — за проход продвигаются на одно ребро.
Гарантий это не даёт, поэтому оценка остаётся . Но на практике ранний выход почти всегда экономит много.
Какой алгоритм когда
| Дейкстра | Форд — Беллман | Флойд | |
|---|---|---|---|
| откуда пути | из одной вершины | из одной вершины | все пары |
| отрицательные рёбра | нельзя | можно | можно |
| ловит отрицательный цикл | нет | да | да |
| сложность |
Правило: отрицательных рёбер нет — Дейкстра. Есть, и нужна одна вершина — Форд — Беллман. Есть, и нужны все пары — Флойд.
Отдельный приём: если отрицательные рёбра есть, но нужно много запусков из разных вершин, применяют алгоритм Джонсона — один запуск Форда — Беллмана пересчитывает веса в неотрицательные, после чего работает Дейкстра.