Context:FIT2004_MOC · the W11 max-flow engine — repeatedly find an s⇝t path in the residual network by DFS and push its bottleneck · proved optimal by the Min-Cut Max-Flow Theorem · reused unmodified by every reduction · PT-03: run max-flow/min-cut by hand, from a blank or a mid-stage network
Parent Framework:Graph
Quick Revision
🎯 Objective:∣f∣=0 ➔ while the residual network has an s⇝t path, push its bottleneck and update both edge directions ➔ no path left ⟹ the flow is maximum.
⚡ Key Constraint: the backward edge is the whole method — pushing only along unused capacity sticks at 19 on the lecture graph; cancelling 4 units on c→b reaches 23. And O(FE) is pseudo-polynomial: F is a value, not an input size.
📝 How It Works
1. The Flow Network and Its Two Constraints
Flow network ➔ directed graph, every edge weighted by a non-negative capacityc(u,v) · sources has no incoming edges · targett has no outgoing edges.
Flow ➔ f(u,v), drawn f/c on the edge; an edge carrying nothing may be drawn as bare c.
Flow conservation ➔ every v∈/{s,t}: ∑Ein(v)f=∑Eout(v)f · lecture check at b: in 8+4+0=12, out 1+11=12.
Value ➔ ∣f∣= net flow out of s= net flow into t (equal by Min-Cut Max-Flow Theorem §2, applied P1).
Max-flow problem ➔ find a valid f maximising ∣f∣ · the value is unique, the assignment is not (the lecture’s run and MUA’s slides both reach 23 with different edge flows).
2. The Residual Network Gf(a.k.a. potential network)
Same vertices, edges re-derived from the current flow ➔ for each u→v in G:
forward / residual edgeu→v of capacity c(u,v)−f(u,v) ➔ room still unused.
backward / reversible edgev→u of capacity f(u,v) ➔ flow already sent that may be cancelled.
Drop zeros ➔ a saturated edge has no forward edge; an empty edge has no backward edge.
Merge parallels ➔ Gf is simple: a→b (c=10, f=0) and b→a (c=4, f=1) give r(a,b)=10+1=11, r(b,a)=3+0=3.
Self-check ➔ r(u,v)+r(v,u)=c(u,v)+c(v,u) — the slide’s “sum of the edges between 2 vertices same as the edge capacity”.
Reading G back off Gf ➔ flow = backward weight · capacity = forward + backward (the W11 sanity-check sheet writes “flow = forward” — a slip; forward is c−f).
Size ➔ at most 2E edges ⟹ built in O(V+E).
3. Path Augmentation
Augmenting path ➔ any s⇝t path in Gf, found by BFS or DFS (BFS “is the best”) · forward and backward edges are traversed alike — “no discrimination”; they differ only at update time.
Bottleneck ➔ b=minresidual capacity along the path — the most that fits through every edge.
Augment ➔ each path edge: forward ⟹ f(u,v)+=b · backward ⟹ f(v,u)−=b ➔ ∣f∣+=b ➔ refresh both residual directions · O(1) per edge — each residual edge holds a pointer to its twin, so no search (§Core Implementation).
What a backward edge means in G ➔ re-routing, not reverse flow. Lecture, from ∣f∣=19: path s→b→c→t (b=min(5,4,5)=4) uses backward b→c ⟹ c→b drops 4→0, c sends those 4 to t instead, and s→b (8→12) refills b‘s lost inflow ⟹ ∣f∣=23.
Why a greedy push fails ➔ the direct route s→b→d→c→t needs d→c (already 7/7) — “we can’t, over capacity”; only the cancellation finds the extra 4.
4. The Method
Loop ➔ build Gf ➔ while an augmenting path exists: take it, add its bottleneck to the flow, augment Gf ➔ return the flow (lecture ford_fulkerson, slide 120).
Invariant ➔ both constraints hold after every augment — the bottleneck never exceeds any residual capacity, and each interior path vertex gains and loses exactly b.
“Method”, not “algorithm” ➔ path choice is left open; BFS vs DFS changes the iteration count, never the final value (§6).
5. Termination and Correctness
Precondition ➔ every capacity is an integer.
Termination ➔ integer residuals ⟹ b≥1 ⟹ ∣f∣ rises by ≥1 per iteration · ∣f∣ is bounded (by the capacity out of s, or any cut) ⟹ at most F iterations.
Optimality ➔ termination alone does not prove maximum; that needs the Min-Cut Max-Flow Theorem — the vertices still reachable from s form a cut whose capacity equals ∣f∣ ➔ its §Proof Blueprint.
W3 shape ➔ correctness = termination + a property at exit, exactly as in Invariant.
Integrality bonus ➔ integer capacities ⟹ FF returns an integer flow on every edge — what lets reductions read matchings and 0/1 entries straight off the flow.
6. Choosing the Path (slides flag as FIT3155 optimisation)
Why path choice matters ➔ each iteration adds between 1 and F; increments 8,13,41,… finish far sooner than 1,2,3,…
Fewest edges ➔ BFS ⟹ Edmonds–Karp ⟹ O(VE2), independent of F (proof in FIT3155) · fewer edges, lower chance of a small capacity on the path.
Largest bottleneck ➔ the lecture’s route is a maximum spanning tree — the bottleneck idea from Minimum Spanning Tree §4.
🔭 Beyond the lecture (the sanity check poses "explain O(VE2)" as D/HD; the argument is not in the provided material)
Distances never shrink ➔ augmenting along BFS shortest paths keeps every δf(s,v) in Gf non-decreasing.
Each edge is the bottleneck ≤V/2 times ➔ a bottleneck edge u→v leaves Gf; it returns only after a later path uses v→u, which pushes δ(s,u) up by ≥2, and distances are capped at V.
Count ➔ ≤2E residual edges ×V/2 ⟹ O(VE) augmentations ×O(E) per BFS ⟹ O(VE2), independent of F.
⚙️ Core Implementation
🔹 Ford-Fulkerson with BFS (applied P8) — paired residual edges
Code
from collections import dequeclass Edge: def __init__(self, v, cap, rev): self.v, self.cap, self.rev = v, cap, rev # cap = RESIDUAL capacity; rev = twin's index in adj[v]def add_edge(adj, u, v, c): adj[u].append(Edge(v, c, len(adj[v]))) # forward: spare = c adj[v].append(Edge(u, 0, len(adj[u]) - 1)) # backward: cancellable = 0def ford_fulkerson(adj, s, t): flow = 0 while True: parent = [None] * len(adj) # rebuilt EVERY iteration parent[s] = (s, None) q = deque([s]) # q.pop() instead => DFS variant while q and parent[t] is None: # 1. augmenting path O(V+E) u = q.popleft() for e in adj[u]: if e.cap > 0 and parent[e.v] is None: parent[e.v] = (u, e) q.append(e.v) if parent[t] is None: # no path => maximum return flow b, v = float('inf'), t # 2. bottleneck O(V) while v != s: u, e = parent[v] b = min(b, e.cap) v = u v = t # 3. augment O(V) while v != s: u, e = parent[v] e.cap -= b # forward loses b adj[v][e.rev].cap += b # twin gains b (cancellable) v = u flow += b
Edge flow afterwards ➔ f(u,v)=c(u,v)−e.cap on each forward edge.
Twins replace merging ➔ antiparallel input edges keep separate twins instead of one merged residual edge — same reachability, same answer.
💡 Common Mistake:Updating only e.cap ➔ without adj[v][e.rev].cap += b no backward edge ever appears and the method degenerates into the greedy push that sticks at 19.
Pseudo-polynomial ➔ F is a number in the input; capacities near 232 make F exponential in the input’s bit-length (Algorithmic Complexity) even though V and E are tiny.
Aug 1: Aug 2: Stop: s→a→b→d→t,b=min(2,1,1,3)=1⇒b→a1→0,b→d3→4,d→t0→1,∣f∣=6s→a→d→t,b=min(1,3,2)=1⇒d→a3→2,s→a2→3,d→t1→2,∣f∣=7s→a3/3 and s→b4/4⇒s has no residual out-edge
Final Extracted Output:∣f∣=7 · both augments run through a backward edge (a→b, then a→d) · other paths give other valid final networks ➔ min cut ({s},rest) in Min-Cut Max-Flow Theorem §Applied.
⚠️ Common Mistakes
💡 Bottleneck from original capacities ➔ use residual ones: prep s→a has c=3 but residual 2.
💡 Drawing a backward edge into G ➔ a backward edge lives only in Gf; augmenting along it decreases the flow on the real edge, it never creates a reverse edge.
💡 Calling O(FE) polynomial ➔ it is pseudo-polynomial; the polynomial bound needs BFS (O(VE2)).
💡 Mid-stage start with forward edges only ➔ a PT-03 network that already carries flow usually needs a backward augment; verify by re-running from blank — the max-flow value must match.
💡 “The max flow” ➔ the value is unique, the edge assignment is not — mark schemes accept any valid final network.
🧠 Active Recall
Why does the residual network need backward edges? Give the lecture case that breaks without them.
Answer
Short answer: an early path can route flow badly; backward edges let a later path cancel it, which no forward-only search can do.
Why:At ∣f∣=19 ➔ every forward route to t is blocked (d→c is 7/7), so a forward-only search stops at 19. Backward b→c (=f(c,b)=4) ➔ path s→b→c→t moves c‘s 4 units off c→b onto c→t and refills b from s ⟹ 23. Optimality depends on it ➔ the Min-Cut Max-Flow Theorem proof needs “no residual path” to mean every T→S edge is empty, which only holds when cancellation was possible.
Why is Ford-Fulkerson O(FE), and why is that pseudo-polynomial?
Hint: count iterations by the flow, not by the graph.
Answer
Short answer:≤F iterations of O(V+E) each; F is a capacity value, so it can be exponential in the input’s bit-length.
Why:Per iteration ➔ one BFS/DFS over Gf (≤2E edges) +O(V) to read and augment the path. Iterations ➔ integer capacities make each bottleneck ≥1, so at most F rounds. Illustration(not in the slides) ➔ diamond s→a, s→b, a→t, b→t at 106 plus a→b at 1: a DFS alternating s→a→b→t and s→b→a→t augments by 1 each time ⟹ 2×106 rounds, while BFS takes the two 2-edge paths ⟹ 2 rounds.
BFS or DFS for the augmenting path — does it change correctness, the answer, or the bound?
Answer
Short answer: neither correctness nor the value; only the bound — any path gives O(FE), BFS adds O(VE2).
Why:Correctness ➔ the proof uses only “no augmenting path at exit”, which holds for any search. Value ➔ the max flow equals the min cut capacity, a property of the network alone. Bound ➔ BFS picks a fewest-edge path (Edmonds–Karp), capping iterations at O(VE) independent of F — the FIT3155 result the slides quote.