490 likes | 886 Views
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.
E N D
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 c(s,a) = 2 c(s,b) = 5 c(a,b) = 1 c(a,t) = 4 c(b,t) = 3 2 s t 1 5 3 b
Flow Constraints f(s,a) = 1 f(a,s) = -1 f(a,b) = 1 f(b,a) = -1 f(b,t) = 1 f(t,b) = -1 a 1|2 4 s t 1|1 5 1|3 b 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.
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 networkGf= (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 3 b a 4 1|2 s t 1|1 5 1|3 graph with flow b
Example (2) graph with flow a 4 1|2 s t 1|1 5 1|3 b a 1 4 1 s t 1 2 residual graph 5 1 b
Example (3) residual graph, with flow-augmenting path a 1|1 1|4 1 s t 1 2 5 1 a b 2|2 1|4 s t 1|1 original graph with new flow 5 1|3 b
Example (4) original graph with new flow a 2|2 1|4 s t 1|1 5 1|3 a b 2 3 1 s t 1 2 new residual graph 5 1 b
Example (5) new residual graph, with augmenting path a 2 3 1 s t 1 2 5 1 a b 2|2 2|4 s t 1 original graph with new flow 1|3 1|5 b
Example (6) original graph with new flow a 2|2 2|4 s t 1 1|3 1|5 a b 2 2 2 s t 1 1 1 new residual graph 2 4 b`
Example (7) new residual graph, with augmenting path a 2 2 2 s t 1 1 1 a 2 4 2|2 2|4 b s t 1 original graph with new flow 3|3 2|5 b
Example (8) original graph, with new flow a 2|2 2|4 s t 1 3|3 2|5 a b 2 2 2 s t 1 2 residual graph (maximum flow = 5) 2 3 b
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) 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 v4 v1 2|4 2/3 source 1|3 1|1 target 2 3 v6 v0 v3 3 3 ∞ 1 1|3 1|4 v2 1 v7 1 v5 = capTo 1 queue = { v0 } = prev
Application to Augmenting Path v4 2 v1 2|4 2/3 source 1|3 1|1 target 2 3 v6 v0 v3 3 3 ∞ 1 1|3 1|4 v2 1 v7 2 1 v5 queue = { v1, v2 }
Application to Augmenting Path v4 2 v1 2|4 2/3 source 1|3 1|1 target 2 3 v6 v0 v3 3 3 ∞ 1 1|3 1|4 v2 3 v7 2 1 v5 queue = {v2}
Application to Augmenting Path v4 2 v1 2|4 2/3 source 1|3 1|1 target 2 3 v6 v0 v3 3 3 ∞ 2 1 1|3 1|4 v2 3 v7 2 1 v5 queue = {v3}
Application to Augmenting Path v4 1 2 v1 2|4 2/3 source 1|3 1|1 target 2 3 v6 v0 v3 3 3 ∞ 2 1 1|3 1|4 v2 1 v7 1 1 1 v5 queue = {v4, v5}
Application to Augmenting Path v4 1 2 Done v1 2|4 2/3 source 1|3 1 1|1 target 2 3 v6 v0 v3 3 3 ∞ 2 1 1|3 1|4 v2 1 v7 1 1 1 v5 queue = { v5, v6 }
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 v4 v1 start v6 v0 v3 v2 v7 v5
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v0 }
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v2 }
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v2, v3 }
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v2, v3 }
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v4, v5 }
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v5 , v6}
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = {v6}
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = {v7}
Breadth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = {}
Depth-first Search • Now see what happens if ToVisit is implemented as a stack (LIFO).
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v0 }
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v2 }
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v3 }
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v4, v5}
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v4 }
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v6}
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1, v7}
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { v1 }
Depth-first Search v4 v1 start v6 v0 v3 v2 v7 v5 ToVisit = { }