Maximum Flow Computation Programming Puzzles and Competitions CIS 4900 / 5920 Spring 2009.

49
Maximum Flow Maximum Flow Computation Computation Programming Puzzles and Competitions Programming Puzzles and Competitions CIS 4900 / 5920 CIS 4900 / 5920 Spring 2009 Spring 2009

Transcript of Maximum Flow Computation Programming Puzzles and Competitions CIS 4900 / 5920 Spring 2009.

Maximum Flow Maximum Flow ComputationComputation

Maximum Flow Maximum Flow ComputationComputation

Programming Puzzles and CompetitionsProgramming Puzzles and CompetitionsCIS 4900 / 5920CIS 4900 / 5920

Spring 2009Spring 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)

s

b

t

a

5

4

3

1

2

Network

c(s,a) = 2c(s,b) = 5c(a,b) = 1c(a,t) = 4c(b,t) = 3

Flow Constraints

s

b

t

a

5

4

1|3

1|1

1|2

f(s,a) = 1 f(a,s) = -1f(a,b) = 1 f(b,a) = -1f(b,t) = 1 f(t,b) = -1

capacity: f(u,v) ≤ c(u,v)symmetry: f(u,v) = -f(v,u)

conservation:

Vvt}{s,Vu

v)f(u,

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

s

b

t

a

5

4

3

1

2

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

5

4

1

1

1|2

1|1

1|3

1

2

1residual 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|1

1

2

1

residual graph, with flow-augmenting path

original graphwith 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

5

3

1

2

2

1

1

Example (4)

s

b

t

a

1|5

2|4

1

2|2

1|3

s

b

t

a

5

3

1

2

2

1

new residual graph, with augmenting path

1

original graphwith 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

4

2

1

2

2

2

11

Example (6)

s

b

t

a

2|5

2|4

1

2|2

3|3

new residual graph, with augmenting path

original graphwith new flow

s

b

t

a

4

2

1

2

2

2

11

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

2

2

1

2

2

3

2

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 basisof the Edmonds-Karp algorithm.

Example: Finding Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = { v0 }

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

1

1

1 = capTo

= prev

Application to Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = { v1, v2 }

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

1

1

2

2

Application to Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = {v2}

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

3

1

2

2

Application to Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = {v3}

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

3

1

2

2

2

Application to Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = {v4, v5}

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

1

1

2

1

1

2

1

Application to Augmenting Path

v0

v2

v3

v1

v4

v5

v6

v7

source

queue = { v5, v6 }

target1|3

1|3 1|4

2/3

1|13

2|4

3

2

1

3

1

1

2

1

1

2

1

1

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 = { }