An – cut is a set , ; its capacity is the total capacity of edges leaving . By max-flow min-cut (Max Flow) the minimum equals the maximum flow, and after any max flow the residual graph describes all minimum cuts at once.
All minimum cuts from one max flow
Fix a maximum flow and its residual graph .
- is the source side of a minimum cut iff , and no residual edge leaves (Picard–Queyranne). So minimum cuts are the closed sets of separating from , which form a lattice under and .
- Smallest source side: = vertices reachable from in . Largest: , where = vertices that can reach in .
- The minimum cut is unique iff .
- Edge is in every minimum cut iff and .
- Edge is in some minimum cut iff it is saturated and , lie in different SCCs of .
- Counting minimum cuts is p-hard in general (closed sets of a DAG), but enumeration or checks per edge are linear after the flow.
struct CutInfo { // call after D.maxflow(s, t)
vector<char> fromS, toT; vector<int> comp;
CutInfo(const Dinic& D, int s, int t) {
int n = sz(D.g); fromS.assign(n, 0); toT.assign(n, 0);
auto bfs = [&](int src, vector<char>& vis, bool rev) {
vector<int> st{src}; vis[src] = 1;
while (sz(st)) {
int u = st.back(); st.pop_back();
for (int id : D.g[u]) {
int v = D.e[id].to; ll c = rev ? D.e[id ^ 1].cap : D.e[id].cap; // rev: residual edge v -> u
if (c > 0 && !vis[v]) vis[v] = 1, st.push_back(v);
}
}
};
bfs(s, fromS, false); bfs(t, toT, true);
comp.assign(n, -1); vector<int> low(n), num(n, -1), stk; int timer = 0, nc = 0; // Tarjan SCC on G_f
auto dfs = [&](auto& self, int u) -> void {
num[u] = low[u] = timer++; stk.push_back(u);
for (int id : D.g[u]) if (D.e[id].cap > 0) {
int v = D.e[id].to;
if (num[v] < 0) self(self, v), low[u] = min(low[u], low[v]);
else if (comp[v] < 0) low[u] = min(low[u], num[v]);
}
if (low[u] == num[u]) { int v; do { v = stk.back(); stk.pop_back(); comp[v] = nc; } while (v != u); nc++; }
};
for (int v = 0; v < n; v++) if (num[v] < 0) dfs(dfs, v);
}
bool unique() const { for (int v = 0; v < sz(fromS); v++) if (!fromS[v] && !toT[v]) return false; return true; }
bool inEvery(int u, int v) const { return fromS[u] && toT[v]; }
bool inSome(const Dinic& D, int id) const { // id = forward edge id u -> v with positive capacity
int u = D.e[id ^ 1].to, v = D.e[id].to;
return D.e[id].cap == 0 && comp[u] != comp[v];
}
};Proof: minimum cuts are the closed sets of the residual graph
For any cut , value, with equality iff every edge leaving is saturated and every edge entering carries no flow, i.e. iff no residual edge leaves . Since value equals the minimum cut capacity, is a minimum cut iff it is closed in . Closed sets containing contain ; closed sets avoiding avoid (if could reach , closure would put in ). Hence the extremes.
Every: if and , every minimum cut contains and excludes . Conversely if the cut misses ; if the cut contains .
Some: if is in a minimum cut it must be saturated (no residual edge leaves ), and cannot reach in because is closed, and ; so they are in different SCCs. Conversely let be saturated with in different SCCs. Saturated with positive capacity means positive flow, so the residual edge exists, and therefore cannot reach . Decompose ; some path or cycle uses . A cycle gives residual reverse edges forming , impossible, so a path carries flow and its reversed edges give residual paths and . Take , which is closed. If then or reaches and hence (via ); reaching means ; all contradict . Likewise . So is a minimum cut containing but not .
Menger and connectivity
- Edge version: the maximum number of edge-disjoint – paths equals the minimum number of edges whose removal separates from (unit capacities).
- Vertex version: for non-adjacent , the maximum number of internally vertex-disjoint paths equals the minimum vertex separator (split into with capacity 1, other edges ).
- Vertex connectivity : a min vertex separator misses one of any vertices, so trying sources against all non-adjacent targets suffices. Edge connectivity is a global min cut (Global Min Cut). Whitney: .
int vertexConnectivity(int n, const vector<vector<int>>& adj) { // undirected, adjacency matrix 0/1
int best = n - 1; // complete graph
for (int s = 0; s <= min(best, n - 1); s++)
for (int t = 0; t < n; t++) {
if (t == s || adj[s][t]) continue;
Dinic D(2 * n); const ll INF = n;
for (int v = 0; v < n; v++) D.addEdge(2 * v, 2 * v + 1, v == s || v == t ? INF : 1);
for (int u = 0; u < n; u++) for (int v = 0; v < n; v++) if (adj[u][v]) D.addEdge(2 * u + 1, 2 * v, INF);
best = min<ll>(best, D.maxflow(2 * s + 1, 2 * t));
}
return best;
}Proof: Menger's theorems
Edge version: integral max flow with unit capacities decomposes into value-many edge-disjoint paths (flow decomposition, cycles discarded), and a min cut with unit capacities is a set of that many edges whose removal separates from ; max-flow min-cut gives equality. Vertex version: in the split graph, finite cuts consist only of edges (all others are , and themselves are ), i.e. vertex separators not containing (exists since are non-adjacent), and unit flow paths through are internally vertex-disjoint. Connectivity loop: let be a minimum separator, at all times. Among some ; has a component not containing , and any there is non-adjacent to and separated by , so the pair reports . If is complete no separator exists and by convention.
Tricks
- Minimum cut with fewest edges: use capacities ; the cut value divided by is the old minimum and the remainder is the number of edges.
- Lexicographic / “edge in a min cut?”: after computing , raising by one changes the max flow iff is in every min cut; deleting drops it by iff is in some min cut (the residual checks above do both in linear time).
- Planar – min cut = shortest path in the dual (grid “block every path” problems): put and on the outer face, split the outer face into two dual vertices, and run Dijkstra on the dual with edge weights = capacities. instead of a flow.
- Cuts with a constraint ” and on the same side”: edges with ; ” on the source side” is with .
- Minimum -cut, multiway cut: NP-hard in general; for fixed tiny use Gomory–Hu heuristics (a approximation by taking the lightest tree edges).
- Gomory–Hu on a planar graph builds the same all-pairs min-cut tree in total instead of general max-flow calls, by exploiting planar duality (each min cut corresponds to a shortest cycle in the dual graph, found via multiple-source shortest paths). Cited here as the planar specialisation of the Gomory–Hu tree above; a from-scratch implementation is rarely worth it outside the handful of contests that specifically require it.