2. 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”)
3. 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)
5. 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:
V
v
t}
{s,
V
u
v)
f(u,
6. Applications
• fluid in pipes
• current in an electrical circuit
• traffic on roads
• data flow in a computer network
• money flow in an economy
• etc.
7. 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?
8. 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
9. 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
18. 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
19. 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;
}}}
20. 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.
27. 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
28. 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.
29. 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.