← Все вопросы

Как реализовать алгоритм Эдмондса-Карпа (BFS) для максимального потока и почему он O(V·E²)?

Задан 16 месяцев назад1.3к просмотров2 ответа
10

Хочу аккуратную реализацию максимального потока, которая гарантированно завершается за полином (Форд-Фалкерсон с DFS может зависать на иррациональных/больших пропускных). Слышал, что нужно искать кратчайший увеличивающий путь через BFS — это Эдмондс-Карп. Дайте рабочий C++ и поясните оценку O(VE2)O(VE^2).

2 ответа

17
✓ Принятый ответ — помог автору

Идея: Эдмондс-Карп — это Форд-Фалкерсон, где увеличивающий путь всегда берётся как кратчайший по числу рёбер (BFS в остаточной сети). Это убирает патологии и даёт полиномиальную оценку.

#include <bits/stdc++.h>
using namespace std;
struct Edge { int to, cap; };
vector<Edge> e;
vector<vector<int>> g;
int n;

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});
}

int maxflow(int s, int t){
    int flow = 0;
    while(true){
        vector<int> par(n, -1); // индекс ребра, по которому пришли
        par[s] = -2;
        queue<int> q; q.push(s);
        while(!q.empty()){
            int v = q.front(); q.pop();
            for(int id : g[v]){
                int to = e[id].to;
                if(par[to]==-1 && e[id].cap>0){
                    par[to]=id; q.push(to);
                }
            }
        }
        if(par[t]==-1) break; // путей нет — поток максимален
        // минимум остаточной по пути
        int push = INT_MAX;
        for(int v=t; v!=s; ){ int id=par[v]; push=min(push,e[id].cap); v=e[id^1].to; }
        for(int v=t; v!=s; ){ int id=par[v]; e[id].cap-=push; e[id^1].cap+=push; v=e[id^1].to; }
        flow += push;
    }
    return flow;
}

Почему O(VE2)O(VE^2). Можно доказать, что расстояние от ss до любой вершины в остаточной сети не убывает по ходу алгоритма. Ребро называется «критическим», когда именно оно даёт минимум на увеличивающем пути (после пуска оно исчезает). Каждое ребро может стать критическим не более O(V)O(V) раз. Рёбер O(E)O(E), значит всего итераций (увеличивающих путей) — O(VE)O(VE). Каждый BFS стоит O(E)O(E). Итог — O(VE2)O(VE^2) по времени, O(V+E)O(V+E) по памяти.

Граблю проверьте: при больших пропускных используйте long long для push и flow, иначе переполнение.

6

Важное преимущество Эдмондса-Карпа над «наивным» Форд-Фалкерсоном с DFS: его сложность не зависит от величины пропускных способностей (нет множителя U=maxcapU = \max cap). У наивного DFS-варианта оценка O(Eflow)O(E \cdot \text{flow}) может быть огромной, если поток большой, и теоретически он даже не завершается на иррациональных capacity. На практике в CP, если граф маленький, а нужна простота — пишите Эдмондса-Карпа; если V,EV, E большие — переходите на Диница (O(V2E)O(V^2E)), он почти всегда быстрее на реальных тестах.

Ваш ответ

Войдите, чтобы ответить на вопрос.