Рассмотрим ориентированный граф
Заметим, что рёбра отрицательной стоимости по условию возможны. Мы дополнительно предполагаем, что циклов отрицательного веса нет.
Модифицируем сеть, добавив стандартным образом обратные рёбра, позволяющие «отменять» операции: для каждого ребра
Если в остаточной сети нет циклов отрицательного веса, то поток оптимален (и наоборот).
Доказательство:
Этот критерий сразу даёт нам относительно простой алгоритм: найдем какой-нибудь максимальный поток и будем «отменять» циклы отрицательного веса в остаточной цепи, пока такие циклы существуют. Искать цикл нам придётся не более
Если искать цикл алгоритмом Форда-Беллмана, то асимптотика алгоритма составит
Вспомним общий алгоритм поиска максимального потока, основанный на теореме Форда-Фалкерсона: найти какой-нибудь дополняющий путь, пропустить по нему поток и модифицировать сеть, снова найти дополняющий путь и так далее, пока путь из истока в сток существует. Что будет, если мы каждый раз будем искать не произвольный путь, а путь минимальной стоимости? Утверждается, что такой алгоритм найдет максимальный поток минимальной стоимости.
Утверждение. Алгоритм не создает в остаточной сети циклов отрицательного веса.
Изначально в остаточной сети нет циклов отрицательного веса. Мы нашли минимальный путь из
Для поиска дополняющего пути можно использовать алгоритм Форда-Беллмана. Асимптотика в данном случае составит
Почему мы не использовали алгоритм Дейкстры? Проблема в рёбрах отрицательного веса. Даже если в исходном графе их нет, они могут в процессе алгоритма появиться как обратные. Покажем, как изменить веса рёбер так, чтобы они стали неотрицательными, но информация о кратчайших путях не утратилась: это можно сделать, если дать каждой вершине так называемый «потенциал», который будет учитываться при пересчете стоимостей ребер.
Потенциалом вершины
Утверждение 1. Веса всех рёбер графа неотрицательные.
Доказательство. Пусть вес какого-то ребра
Аналогично можно показать, что рёбра на кратчайших путях из
Утверждение 2. Кратчайшие пути между любыми вершинами остались кратчайшими.
Доказательство. Распишем новую стоимость пути из
Получаем, что стоимость всех путей из
Более того, если мы добавим или удалим некоторые рёбра из графа, потенциалы тоже никак не повлияют на кратчайшие пути.
Заметьте, что в доказательстве мы не использовали то, что
Утверждение 3. Когда мы проталкиваем поток вдоль кратчайшего пути, удаляя ребра и возможно добавляя обратные, веса в изменённом графе тоже остались корректными (все рёбра неотрицательного веса и все кратчайшие пути остались кратчайшими).
Доказательство. Все добавленные обратные рёбра на кратчайшем пути будут иметь нулевую стомость (утверждение 1), а добавления или удаления рёбер на кратчайшие пути не повлияли (утверждение 2).
- Модифицируем сеть, добавив обратные рёбра.
- Если в исходном графе есть рёбра отрицательного веса (но нет циклов отрицательного веса), то посчитать изначальные потенциалы (расстояния) алгоритмом Форда-Беллмана. Иначе достаточно положить потенциалы изначально равными нулю.
- Пока максимальный поток не найден:
-
- Посчитать алгоритмом Дейкстры кратчайшие расстояния от
$s$ , используя для веса формулу с потенциалами, записать их в$d$ .
- Посчитать алгоритмом Дейкстры кратчайшие расстояния от
-
- Протолкнуть максимально возможный поток вдоль кратчайшего пути
$s \leadsto t$ , обновить остаточную сеть.
- Протолкнуть максимально возможный поток вдоль кратчайшего пути
Ниже приведено решение задачи о назначениях (паросочетание минимального веса). Для нахождения дополняющего пути используется алгоритм Дейкстры для плотных графов (без приоритетной очереди — каждую итерацию ищется минимум за $O(n)$).
cost
,cap
— параметры сетиpot
— потенциалыpar
— предок вершины в алгоритме Дейкстры (нужен для проталкивания потока)d
— временный массив для алгоритма Дейкстры, куда будут записаны новые расстояния
const int maxn = 305, inf = 1e9;
int n;
int cost[maxn][maxn], cap[maxn][maxn];
int d[maxn], pot[maxn], par[maxn];
bool dijkstra (int s, int t) {
used[maxn] = {0};
fill(d, d+n, inf);
d[s] = 0;
while (1) {
int v = -1;
for (int u = 0; u < n; u++)
if (!used[u] && (v == -1 && d[u] < d[v]))
v = u;
if (v == -1 || d[v] == inf)
break;
used[v] = 1;
for (int u = 0; u < n; u++) {
int w = cost[v][u] + pot[v] - pot[u];
if (cap[v][u] && d[u] > d[v] + w) {
d[u] = d[v] + w;
par[u] = v;
}
}
}
return d[t] < inf;
}
int mincost_maxflow (int s, int t) {
int ans = 0;
while (dijkstra(s, t)) {
memcpy(pot, d, sizeof(d));
int delta = inf;
for (int v = t; v != s; v = par[v])
delta = min(delta, cap[par[v]][v]);
for (int v = t; v != s; v = par[v]) {
cap[par[v]][v] -= delta;
cap[v][par[v]] += delta;
ans += cost[par[v]][v]*delta;
}
}
return ans;
}
В общем случае, алгоритм работает за
В наиболее популярных задачах рёбра обычно с единичной пропускной способностью, и