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”)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)
s b t a Network c(s,a) = 2 c(s,b) = 5 c(a,b) = 1 c(a,t) = 4 c(b,t) = 3
Flow Constraints s b t a 5 4 1|3 1|1 1|2 f(s,a) = 1 f(a,s) = -1 f(a,b) = 1 f(b,a) = -1 f(b,t) = 1 f(t,b) = -1 capacity: f(u,v) ≤ c(u,v) symmetry: f(u,v) = -f(v,u) conservation:
Applications fluid in pipes current in an electrical circuit traffic on roads data flow in a computer network money flow in an economy etc.
Maximu m 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 c f (u, v) = c(u, v) f (u, v) ≥ 0 since f (u, v) ≤ c(u, v) Residual network G f = (V, E f ) E f = { (u, v) є V ×V | c f (u, v) > 0 } Example: c(u,v) = 16, f(u,v) = 5 c f (u, v) = 11
s b t a Example (1) s b t a 5 4 1|3 1|1 1|2 graph with flow original graph
s b t a s b t a |2 1|1 1| residual graph graph with flow 5 4 Example (2)
s b t a 5 1|4 1|1 2|2 1|3 s b t a 5 1|4 1 1| residual graph, with flow-augmenting path original graph with new flow Example (3)
s b t a 5 1|4 1|1 2|2 1|3 original graph with new flow new residual graph s b t a Example (4)
s b t a 1|5 2|4 1 2|2 1|3 s b t a new residual graph, with augmenting path 1 original graph with new flow Example (5)
s b t a 1|5 2|4 1 2|2 1|3 original graph with new flow new residual graph s b` t a Example (6)
s b t a 2|5 2|4 1 2|2 3|3 new residual graph, with augmenting path original graph with new flow s b t a Example (7)
s b t a 2|5 2|4 1 2|2 3|3 original graph, with new flow residual graph (maximum flow = 5) Example (8) s b t a
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 G f ) { c f (p) = min {c f (u,v) | (u,v) є p}; for (each edge (u,v) є p) { f[u][v] = f[u][v] + c f (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 findMaxFlow (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 = findAugmentingPath(s, t); if (Increment == 0) return result; result += capTo[t]; int v = t, u; while (v != s) { // augment flow along path u = prev[v]; flow[u][v] += capTo[t]; flow[v][u] -= capTo[t]; v = u; }}}
static int findAugmentingPath(int s, int t) { for (int i = 0; i < n; i++) { prev[i] = -1; capTo[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 edgeCap = a[u][v] - flow[u][v]; if ((prev[v] == -1) && (edgeCap > 0)) { capTo[v] = Math.min(capTo[u], edgeCap); prev[v] = u; if (v == t) return capTo[v]; queue[last++] = v; }}}} return 0; } This uses breadth-first search, which is the basis of the Edmonds-Karp algorithm.
Example: Finding Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = { v0 } target 1|3 1|4 2/3 1|1 3 2| ∞ 1 = capTo = prev
Application to Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = { v1, v2 } target 1|3 1|4 2/3 1|1 3 2| ∞ 2 2
Application to Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = {v2} target 1|3 1|4 2/3 1|1 3 2| ∞ 2 2
Application to Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = {v3} target 1|3 1|4 2/3 1|1 3 2| ∞ 2 2 2
Application to Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = {v4, v5} target 1|3 1|4 2/3 1|1 3 2| ∞
Application to Augmenting Path v0 v2 v3 v1 v4 v5 v6 v7 source queue = { v5, v6 } target 1|3 1|4 2/3 1|1 3 2| ∞ Done
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 findAugmentingPath(int s, int t) { for (int i = 0; i < n; i++) { prev[i] = -1; capTo[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 edgeCap = a[u][v] - flow[u][v]; if ((prev[v] == -1) && (edgeCap > 0)) { capTo[v] = Math.min(capTo[u], edgeCap); prev[v] = u; if (v == t) return capTo[v]; queue[last++] = v; }}}} return 0; } This uses depth-first search.
Breadth vs. Depth-first Search Let s be the start node ToVisit.make_empty; ToVisit.insert(s); s.marked = true; while not ToVisit.is_empty { u = ToVisit.extract; for each edge (u,v) in E if not u.marked { u.marked = true; ToVisit.insert(u);} If Bag is a FIFO queue, we get breadth-first search; if LIFO (stack), we get dept-first.
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v0 }
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v2 }
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v2, v3 }
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v2, v3 }
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v4, v5 }
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v5, v6}
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = {v6}
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = {v7}
Breadth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = {}
Depth-first Search Now see what happens if ToVisit is implemented as a stack (LIFO).
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v0 }
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v2 }
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v3 }
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v4, v5}
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v4 }
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v6}
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1, v7}
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { v1 }
Depth-first Search v0 v2 v3 v1 v4 v5 v6 v7 start ToVisit = { }