Что такое максимальный поток в сети и из каких частей он состоит (исток, сток, остаточная сеть)?
Готовлюсь к олимпиадам, вижу задачи «найти максимальный поток», но плохо понимаю саму модель. Что такое сеть, исток и сток, пропускные способности? И что за «остаточная сеть», про которую всё время говорят при объяснении Форда-Фалкерсона? Можно строго, но понятно.
2 ответа
Сеть — это ориентированный граф, где у каждого ребра есть пропускная способность . Выделены две вершины: исток (откуда «течёт») и сток (куда «втекает»). Поток — это функция , удовлетворяющая трём условиям:
- Ограничение пропускной способности: .
- Антисимметрия (в одной из формулировок): .
- Сохранение потока: для любой вершины кроме и сумма входящего потока равна сумме исходящего.
Величина потока — это суммарный поток, выходящий из истока (он же равен втекающему в сток).
Остаточная сеть — ключевая идея. Для текущего потока остаточная пропускная способность ребра — это , то есть «сколько ещё можно пустить». Важно: если по ребру уже течёт , то в остаточной сети появляется обратное ребро с пропускной способностью — оно позволяет «отменить» (откатить) часть ранее пущенного потока. Именно поэтому жадность без обратных рёбер не работает, а с ними — даёт оптимум.
Алгоритмы (Форд-Фалкерсон, Эдмондс-Карп, Диниц) ищут увеличивающий путь в остаточной сети и пускают по нему поток, пока такие пути есть. Когда путей нет — поток максимален.
Добавлю практическую деталь, на которой все спотыкаются при первой реализации. Обратные рёбра удобно хранить парами в одном массиве рёбер: ребро с индексом и его обратное с индексом (XOR с единицей). Тогда edges[i^1] — это всегда «обратное» к edges[i]. При добавлении ребра сразу кладём два:
struct Edge { int to, cap; };
vector<Edge> e;
vector<vector<int>> g; // g[v] — индексы рёбер из v
void add_edge(int u, int v, int cap) {
g[u].push_back(e.size()); e.push_back({v, cap});
g[v].push_back(e.size()); e.push_back({u, 0}); // обратное, cap=0
}
Когда пускаем по ребру : e[i].cap -= d; e[i^1].cap += d;. Это и есть обновление остаточной сети. Обратное ребро стартует с нулевой пропускной — но по мере пуска прямого потока его остаток растёт, и его можно использовать для отката.