Each edge has a capacity and a cost per unit of flow. Find the cheapest way to send a required amount of flow from to — or the cheapest maximum flow (MCMF).

Successive Shortest Paths (SSP)

Repeatedly augment along the cheapest path in the residual graph.

while flow < required and an s->t path exists:
    find the minimum-cost path in the residual graph
    push as much flow along it as capacity allows

Why it is optimal: if the current flow is a minimum-cost flow of its value, augmenting along a shortest path yields a minimum-cost flow of the new value. Inductively the answer is optimal at every value — so SSP also solves the parametric problem of “cheapest flow of value ” for all at once.

Johnson potentials — using Dijkstra instead of Bellman-Ford

Residual graphs have negative edges (the reverse of a positive-cost edge costs ), so Dijkstra cannot be used directly. Fix it with potentials:

Initialise with one Bellman-Ford run (or with zeros if all costs are non-negative), then after each Dijkstra set . Reduced costs stay non-negative, and the shortest paths are unchanged — the same Johnson reweighting used for all-pairs shortest paths.

struct MCMF {
    struct E { int to; long long cap, cost; };
    vector<E> es; vector<vector<int>> g;
    vector<long long> pot, dist; vector<int> pv;
    int n;
 
    MCMF(int n) : g(n), pot(n, 0), dist(n), pv(n), n(n) {}
    void addEdge(int u, int v, long long cap, long long cost) {
        g[u].push_back(es.size()); es.push_back({v, cap,  cost});
        g[v].push_back(es.size()); es.push_back({u, 0,   -cost});
    }
 
    pair<long long,long long> run(int s, int t, long long need = LLONG_MAX) {
        long long flow = 0, cost = 0;
        // if negative costs exist, initialise pot[] with Bellman-Ford here
        while (flow < need) {
            dist.assign(n, LLONG_MAX);
            priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
            dist[s] = 0; pq.push({0, s});
            while (!pq.empty()) {
                auto [d, u] = pq.top(); pq.pop();
                if (d > dist[u]) continue;
                for (int id : g[u]) {
                    auto& e = es[id];
                    if (e.cap <= 0) continue;
                    long long nd = d + e.cost + pot[u] - pot[e.to];
                    if (nd < dist[e.to]) { dist[e.to] = nd; pv[e.to] = id; pq.push({nd, e.to}); }
                }
            }
            if (dist[t] == LLONG_MAX) break;
            for (int i = 0; i < n; i++) if (dist[i] < LLONG_MAX) pot[i] += dist[i];
 
            long long push = need - flow;
            for (int v = t; v != s; v = es[pv[v] ^ 1].to) push = min(push, es[pv[v]].cap);
            for (int v = t; v != s; v = es[pv[v] ^ 1].to) {
                es[pv[v]].cap -= push; es[pv[v] ^ 1].cap += push;
                cost += push * es[pv[v]].cost;
            }
            flow += push;
        }
        return {flow, cost};
    }
};

Complexity

MethodTime
SSP with Bellman-Ford
SSP with potentials + Dijkstra
Capacity scaling SSP
Cycle cancelling
Min-mean cycle cancellingstrongly polynomial
Cost scaling
Network simplexexponential worst case, fastest in practice

is the total flow. In contest problems is usually (matching-style), which makes SSP comfortably fast.

Modelling patterns

ProblemConstruction
Assignment problem (cap 1, cost 0), (cap 1, cost ), (cap 1, cost 0)
Transportation / supply-demandsupplies from , demands to , shipping costs on edges
vertex-disjoint paths of minimum total costsplit vertices; flow of value — see Suurballe
Minimum cost to satisfy lower boundscirculation with demands
Scheduling with time slotsjobs on one side, slots on the other, costs = penalties
Convex cost functionsreplace one edge by several parallel edges with increasing costs — the SSP order makes the cheapest get used first

That last row is important: piecewise-linear convex costs are modelled by splitting an edge into parallel edges of increasing cost, and min-cost flow handles them automatically. Non-convex costs break this and generally make the problem NP-hard.

Negative costs

Allowed, as long as there is no negative-cost cycle in the initial graph. Initialise potentials with Bellman-Ford. If negative cycles exist, first cancel them (or note that the “minimum cost flow of value 0” is already negative).

See also: Maximum Flow · Hungarian Algorithm · Circulation with Demands