Maximum Flow Computation Programming Puzzles and Competitions CIS

















![Ford-Fulkerson Algorithm for (each edge (u, v) є E[G]) f[u][v] = f[v][u] = 0; Ford-Fulkerson Algorithm for (each edge (u, v) є E[G]) f[u][v] = f[v][u] = 0;](https://slidetodoc.com/presentation_image/4aa7497c6174090c342fa14254e4eeaf/image-18.jpg)































- Slides: 49
Maximum Flow Computation Programming Puzzles and Competitions CIS 4900 / 5920 Spring 2009
Outline • Flow analysis • The min-cut and max-flow problems • Ford-Fulkerson and Edmonds-Karp max-flow algorithms • Start of an example problem from ICPC’ 07 (“Tunnels”)
Flow Network • Directed graph G = (V, E) with – – edge capacities c(u, v) ≥ 0 a designated source node s a designated target/sink node t flows on edges f(u, v)
Network a 4 2 s t 1 5 b 3 c(s, a) = 2 c(s, b) = 5 c(a, b) = 1 c(a, t) = 4 c(b, t) = 3
Flow Constraints 1|2 a s 4 t 1|1 5 b 1|3 capacity: f(u, v) ≤ c(u, v) symmetry: f(u, v) = -f(v, u) conservation: f(s, a) = 1 f(a, s) = -1 f(a, b) = 1 f(b, a) = -1 f(b, t) = 1 f(t, b) = -1
Applications • • • fluid in pipes current in an electrical circuit traffic on roads data flow in a computer network money flow in an economy etc.
Maximum Flow Problem Assuming – source produces the material at a steady rate – sink consumes the material at a steady rate What is the maximum net flow from s to t?
Ford-Fulkerson Algorithm • Start with zero flow • Repeat until convergence: – Find an augmenting path, from s to t along which we can push more flow – Augment flow along this path
Residual Capacity • Given a flow f in network G = (V, E) • Consider a pair of vertices u, v є V • Residual capacity = amount of additional flow we can push directly from u to v cf (u, v) = c(u, v) f (u, v) ≥ 0 since f (u, v) ≤ c(u, v) • Residual network Gf = (V, Ef ) Ef = { (u, v) є V ×V | cf (u, v) > 0 } • Example: c(u, v) = 16, f(u, v) = 5 cf (u, v) = 11
Example (1) original graph a 4 2 s t 1 5 b 3 a 4 1|2 s graph with flow t 1|1 5 b 1|3
Example (2) graph with flow 1|2 a s 4 t 1|1 5 1|3 b a 1 1 s residual graph 4 1 5 t 2 1 b
Example (3) residual graph, with flow-augmenting path a 1|1 1|4 1 s 1 t 2 5 1 b 2|2 a s original graph with new flow 1|4 t 1|1 5 1|3 b
Example (4) original graph with new flow 2|2 a s 1|4 t 1|1 5 1|3 2 b a 1 s new residual graph 3 1 5 b t 2 1
Example (5) new residual graph, with augmenting path 2 a 3 1 s 1 t 2 5 b 1 2|2 a s original graph with new flow 2|4 t 1 1|5 1|3 b
Example (6) original graph with new flow 2|2 a s 2|4 t 1 1|5 1|3 b a 2 s new residual graph 2 1 1 4 2 t 1 2 b`
Example (7) new residual graph, with augmenting path a 2 s 2 1 1 4 2 t 1 2 2|2 b a s original graph with new flow 2|4 t 1 2|5 3|3 b
Example (8) original graph, with new flow 2|2 a s 2|4 t 1 2|5 3|3 a 2 b s residual graph (maximum flow = 5) 2 1 2 t 2 2 b 3
Ford-Fulkerson Algorithm for (each edge (u, v) є E[G]) f[u][v] = f[v][u] = 0; while ( path p from s to t in Gf) { cf(p) = min {cf(u, v) | (u, v) є p}; for (each edge (u, v) є p) { f[u][v] = f[u][v] + cf(p) f[v][u] = -f[u][v] } } O(E) O(E x f*) f* = maximum flow, assuming integer flows, since each iteration increases flow by at least one unit
int find. Max. Flow (int s, int t) { int result = 0; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) flow[i][j] = 0; for (; ; ) { int Increment = find. Augmenting. Path(s, t); if (Increment == 0) return result; result += cap. To[t]; int v = t, u; while (v != s) { // augment flow along path u = prev[v]; flow[u][v] += cap. To[t]; flow[v][u] -= cap. To[t]; v = u; }}}
static int find. Augmenting. Path(int s, int t) { for (int i = 0; i < n; i++) { prev[i] = -1; cap. To[i] = Integer. MAX_VALUE; } int first = 0, last = 0; queue[last++] = s; prev[s] = -2; // s visited already while (first != last) { int u = queue[first++]; for (int v = 0; v < n; v++) { if (a[u][v] > 0) { int edge. Cap = a[u][v] - flow[u][v]; if ((prev[v] == -1) && (edge. Cap > 0)) { cap. To[v] = Math. min(cap. To[u], edge. Cap); prev[v] = u; if (v == t) return cap. To[v]; queue[last++] = v; }}}} return 0; } This uses breadth-first search, which is the basis of the Edmonds-Karp algorithm.
Example: Finding Augmenting Path v 4 source v 1 1|3 1|1 3 3 v 0 ∞ 1|3 2|4 2/3 2 v 6 3 v 3 1 1|4 v 2 target 1 v 7 1 v 5 queue = { v 0 } 1 = cap. To = prev
Application to Augmenting Path v 4 2 source v 1 1|3 1|1 3 3 v 0 ∞ 1|3 2|4 2/3 2 v 6 3 v 3 1 1|4 v 2 1 v 7 2 1 v 5 queue = { v 1, v 2 } target
Application to Augmenting Path v 4 2 source v 1 1|3 1|1 3 3 v 0 ∞ 1|3 2|4 2/3 2 v 6 3 v 3 1 1|4 v 2 3 v 7 2 1 v 5 queue = {v 2} target
Application to Augmenting Path v 4 2 source v 1 1|3 1|1 3 3 v 0 ∞ 1|3 2 2 1 3 v 7 2 1 v 5 queue = {v 3} v 6 3 v 3 1|4 v 2 2|4 2/3 target
Application to Augmenting Path 1 2 source v 1 1|1 3 ∞ 3 1|3 v 2 2 2 1 1 v 7 1 1 v 5 queue = {v 4, v 5} v 6 3 v 3 1|4 1 2|4 2/3 1|3 v 0 v 4 target
Application to Augmenting Path 1 2 source v 1 1|1 3 3 ∞ 1|3 1 v 2 v 6 3 2 1 1 v 7 1 1 v 5 queue = { v 5, v 6 } 1 2 v 3 1|4 Done 2|4 2/3 1|3 v 0 v 4 target
Breadth-first search • • The above is an example Depth-first search is an alternative The code is nearly the same Only the queuing order differs
static int find. Augmenting. Path(int s, int t) { for (int i = 0; i < n; i++) { prev[i] = -1; cap. To[i] = Integer. MAX_VALUE; } int first = 0, last = 0; queue[last++] = s; prev[s] = -2; // s visited already while (first != last) { int u = queue[last--]; for (int v = 0; v < n; v++) { if (a[u][v] > 0) { int edge. Cap = a[u][v] - flow[u][v]; if ((prev[v] == -1) && (edge. Cap > 0)) { cap. To[v] = Math. min(cap. To[u], edge. Cap); prev[v] = u; if (v == t) return cap. To[v]; queue[last++] = v; }}}} return 0; } This uses depth-first search.
Breadth vs. Depth-first Search • Let s be the start node To. Visit. make_empty; To. Visit. insert(s); s. marked = true; while not To. Visit. is_empty { u = To. Visit. extract; for each edge (u, v) in E if not u. marked { u. marked = true; To. Visit. insert(u); }} If Bag is a FIFO queue, we get breadth-first search; if LIFO (stack), we get dept-first.
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 0 }
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 2 }
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 2, v 3 }
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 2, v 3 }
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 4, v 5 }
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 5 , v 6}
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = {v 6}
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = {v 7}
Breadth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = {}
Depth-first Search • Now see what happens if To. Visit is implemented as a stack (LIFO).
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 0 }
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 2 }
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 3 }
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 4, v 5}
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 4 }
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 6}
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1, v 7}
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { v 1 }
Depth-first Search v 4 start v 1 v 0 v 6 v 3 v 2 v 7 v 5 To. Visit = { }