Files
puzzles/maxflow.c
Simon Tatham 669bb81f08 New puzzle: `Tents'. Requires a potentially shared algorithms module
maxflow.c. Also in this checkin, fixes to the OS X and GTK back ends
to get ALIGN_VNORMAL right. This is the first time I've used it! :-)

[originally from svn r6390]
2005-10-13 18:30:24 +00:00

462 lines
10 KiB
C

/*
* Edmonds-Karp algorithm for finding a maximum flow and minimum
* cut in a network. Almost identical to the Ford-Fulkerson
* algorithm, but apparently using breadth-first search to find the
* _shortest_ augmenting path is a good way to guarantee
* termination and ensure the time complexity is not dependent on
* the actual value of the maximum flow. I don't understand why
* that should be, but it's claimed on the Internet that it's been
* proved, and that's good enough for me. I prefer BFS to DFS
* anyway :-)
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "maxflow.h"
#include "puzzles.h" /* for snewn/sfree */
int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
int ne, const int *edges, const int *backedges,
const int *capacity, int *flow, int *cut)
{
int *todo = (int *)scratch;
int *prev = todo + nv;
int *firstedge = todo + 2*nv;
int *firstbackedge = todo + 3*nv;
int i, j, head, tail, from, to;
int totalflow;
/*
* Scan the edges array to find the index of the first edge
* from each node.
*/
j = 0;
for (i = 0; i < ne; i++)
while (j <= edges[2*i])
firstedge[j++] = i;
while (j < nv)
firstedge[j++] = ne;
assert(j == nv);
/*
* Scan the backedges array to find the index of the first edge
* _to_ each node.
*/
j = 0;
for (i = 0; i < ne; i++)
while (j <= edges[2*backedges[i]+1])
firstbackedge[j++] = i;
while (j < nv)
firstbackedge[j++] = ne;
assert(j == nv);
/*
* Start the flow off at zero on every edge.
*/
for (i = 0; i < ne; i++)
flow[i] = 0;
totalflow = 0;
/*
* Repeatedly look for an augmenting path, and follow it.
*/
while (1) {
/*
* Set up the prev array.
*/
for (i = 0; i < nv; i++)
prev[i] = -1;
/*
* Initialise the to-do list for BFS.
*/
head = tail = 0;
todo[tail++] = source;
/*
* Now do the BFS loop.
*/
while (head < tail && prev[sink] <= 0) {
from = todo[head++];
/*
* Try all the forward edges out of node `from'. For a
* forward edge to be valid, it must have flow
* currently less than its capacity.
*/
for (i = firstedge[from]; i < ne && edges[2*i] == from; i++) {
to = edges[2*i+1];
if (to == source || prev[to] >= 0)
continue;
if (capacity[i] >= 0 && flow[i] >= capacity[i])
continue;
/*
* This is a valid augmenting edge. Visit node `to'.
*/
prev[to] = 2*i;
todo[tail++] = to;
}
/*
* Try all the backward edges into node `from'. For a
* backward edge to be valid, it must have flow
* currently greater than zero.
*/
for (i = firstbackedge[from];
j = backedges[i], i < ne && edges[2*j+1]==from; i++) {
to = edges[2*j];
if (to == source || prev[to] >= 0)
continue;
if (flow[j] <= 0)
continue;
/*
* This is a valid augmenting edge. Visit node `to'.
*/
prev[to] = 2*j+1;
todo[tail++] = to;
}
}
/*
* If prev[sink] is non-null, we have found an augmenting
* path.
*/
if (prev[sink] >= 0) {
int max;
/*
* Work backwards along the path figuring out the
* maximum flow we can add.
*/
to = sink;
max = -1;
while (to != source) {
int spare;
/*
* Find the edge we're currently moving along.
*/
i = prev[to];
from = edges[i];
assert(from != to);
/*
* Determine the spare capacity of this edge.
*/
if (i & 1)
spare = flow[i / 2]; /* backward edge */
else if (capacity[i / 2] >= 0)
spare = capacity[i / 2] - flow[i / 2]; /* forward edge */
else
spare = -1; /* unlimited forward edge */
assert(spare != 0);
if (max < 0 || (spare >= 0 && spare < max))
max = spare;
to = from;
}
/*
* Fail an assertion if max is still < 0, i.e. there is
* an entirely unlimited path from source to sink. Also
* max should not _be_ zero, because by construction
* this _should_ be an augmenting path.
*/
assert(max > 0);
/*
* Now work backwards along the path again, this time
* actually adjusting the flow.
*/
to = sink;
while (to != source) {
/*
* Find the edge we're currently moving along.
*/
i = prev[to];
from = edges[i];
assert(from != to);
/*
* Adjust the edge.
*/
if (i & 1)
flow[i / 2] -= max; /* backward edge */
else
flow[i / 2] += max; /* forward edge */
to = from;
}
/*
* And adjust the overall flow counter.
*/
totalflow += max;
continue;
}
/*
* If we reach here, we have failed to find an augmenting
* path, which means we're done. Output the `cut' array if
* required, and leave.
*/
if (cut) {
for (i = 0; i < nv; i++) {
if (i == source || prev[i] >= 0)
cut[i] = 0;
else
cut[i] = 1;
}
}
return totalflow;
}
}
int maxflow_scratch_size(int nv)
{
return (nv * 4) * sizeof(int);
}
void maxflow_setup_backedges(int ne, const int *edges, int *backedges)
{
int i, n;
for (i = 0; i < ne; i++)
backedges[i] = i;
/*
* We actually can't use the C qsort() function, because we'd
* need to pass `edges' as a context parameter to its
* comparator function. So instead I'm forced to implement my
* own sorting algorithm internally, which is a pest. I'll use
* heapsort, because I like it.
*/
#define LESS(i,j) ( (edges[2*(i)+1] < edges[2*(j)+1]) || \
(edges[2*(i)+1] == edges[2*(j)+1] && \
edges[2*(i)] < edges[2*(j)]) )
#define PARENT(n) ( ((n)-1)/2 )
#define LCHILD(n) ( 2*(n)+1 )
#define RCHILD(n) ( 2*(n)+2 )
#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
/*
* Phase 1: build the heap. We want the _largest_ element at
* the top.
*/
n = 0;
while (n < ne) {
n++;
/*
* Swap element n with its parent repeatedly to preserve
* the heap property.
*/
i = n-1;
while (i > 0) {
int p = PARENT(i);
if (LESS(backedges[p], backedges[i])) {
SWAP(backedges[p], backedges[i]);
i = p;
} else
break;
}
}
/*
* Phase 2: repeatedly remove the largest element and stick it
* at the top of the array.
*/
while (n > 0) {
/*
* The largest element is at position 0. Put it at the top,
* and swap the arbitrary element from that position into
* position 0.
*/
n--;
SWAP(backedges[0], backedges[n]);
/*
* Now repeatedly move that arbitrary element down the heap
* by swapping it with the more suitable of its children.
*/
i = 0;
while (1) {
int lc, rc;
lc = LCHILD(i);
rc = RCHILD(i);
if (lc >= n)
break; /* we've hit bottom */
if (rc >= n) {
/*
* Special case: there is only one child to check.
*/
if (LESS(backedges[i], backedges[lc]))
SWAP(backedges[i], backedges[lc]);
/* _Now_ we've hit bottom. */
break;
} else {
/*
* The common case: there are two children and we
* must check them both.
*/
if (LESS(backedges[i], backedges[lc]) ||
LESS(backedges[i], backedges[rc])) {
/*
* Pick the more appropriate child to swap with
* (i.e. the one which would want to be the
* parent if one were above the other - as one
* is about to be).
*/
if (LESS(backedges[lc], backedges[rc])) {
SWAP(backedges[i], backedges[rc]);
i = rc;
} else {
SWAP(backedges[i], backedges[lc]);
i = lc;
}
} else {
/* This element is in the right place; we're done. */
break;
}
}
}
}
#undef LESS
#undef PARENT
#undef LCHILD
#undef RCHILD
#undef SWAP
}
int maxflow(int nv, int source, int sink,
int ne, const int *edges, const int *capacity,
int *flow, int *cut)
{
void *scratch;
int *backedges;
int size;
int ret;
/*
* Allocate the space.
*/
size = ne * sizeof(int) + maxflow_scratch_size(nv);
backedges = smalloc(size);
if (!backedges)
return -1;
scratch = backedges + ne;
/*
* Set up the backedges array.
*/
maxflow_setup_backedges(ne, edges, backedges);
/*
* Call the main function.
*/
ret = maxflow_with_scratch(scratch, nv, source, sink, ne, edges,
backedges, capacity, flow, cut);
/*
* Free the scratch space.
*/
sfree(backedges);
/*
* And we're done.
*/
return ret;
}
#ifdef TESTMODE
#define MAXEDGES 256
#define MAXVERTICES 128
#define ADDEDGE(i,j) do{edges[ne*2] = (i); edges[ne*2+1] = (j); ne++;}while(0)
int compare_edge(const void *av, const void *bv)
{
const int *a = (const int *)av;
const int *b = (const int *)bv;
if (a[0] < b[0])
return -1;
else if (a[0] > b[0])
return +1;
else if (a[1] < b[1])
return -1;
else if (a[1] > b[1])
return +1;
else
return 0;
}
int main(void)
{
int edges[MAXEDGES*2], ne, nv;
int capacity[MAXEDGES], flow[MAXEDGES], cut[MAXVERTICES];
int source, sink, p, q, i, j, ret;
/*
* Use this algorithm to find a maximal complete matching in a
* bipartite graph.
*/
ne = 0;
nv = 0;
source = nv++;
p = nv;
nv += 5;
q = nv;
nv += 5;
sink = nv++;
for (i = 0; i < 5; i++) {
capacity[ne] = 1;
ADDEDGE(source, p+i);
}
for (i = 0; i < 5; i++) {
capacity[ne] = 1;
ADDEDGE(q+i, sink);
}
j = ne;
capacity[ne] = 1; ADDEDGE(p+0,q+0);
capacity[ne] = 1; ADDEDGE(p+1,q+0);
capacity[ne] = 1; ADDEDGE(p+1,q+1);
capacity[ne] = 1; ADDEDGE(p+2,q+1);
capacity[ne] = 1; ADDEDGE(p+2,q+2);
capacity[ne] = 1; ADDEDGE(p+3,q+2);
capacity[ne] = 1; ADDEDGE(p+3,q+3);
capacity[ne] = 1; ADDEDGE(p+4,q+3);
/* capacity[ne] = 1; ADDEDGE(p+2,q+4); */
qsort(edges, ne, 2*sizeof(int), compare_edge);
ret = maxflow(nv, source, sink, ne, edges, capacity, flow, cut);
printf("ret = %d\n", ret);
for (i = 0; i < ne; i++)
printf("flow %d: %d -> %d\n", flow[i], edges[2*i], edges[2*i+1]);
for (i = 0; i < nv; i++)
if (cut[i] == 0)
printf("difficult set includes %d\n", i);
return 0;
}
#endif