The source code and dockerfile for the GSW2024 AI Lab.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 
 
 

392 lines
15 KiB

/* mincut.c */
/* Written by Andrew Makhorin <mao@gnu.org>, October 2015. */
#include <limits.h>
#include "maxflow.h"
#include "mincut.h"
#include "misc.h"
/***********************************************************************
* NAME
*
* min_cut - find min cut in undirected capacitated network
*
* SYNOPSIS
*
* #include "mincut.h"
* int min_cut(int nn, int ne, const int beg[], const int end[],
* const cap[], int cut[]);
*
* DESCRIPTION
*
* This routine finds min cut in a given undirected network.
*
* The undirected capacitated network is specified by the parameters
* nn, ne, beg, end, and cap. The parameter nn specifies the number of
* vertices (nodes), nn >= 2, and the parameter ne specifies the number
* of edges, ne >= 0. The network edges are specified by triplets
* (beg[k], end[k], cap[k]) for k = 1, ..., ne, where beg[k] < end[k]
* are numbers of the first and second nodes of k-th edge, and
* cap[k] > 0 is a capacity of k-th edge. Loops and multiple edges are
* not allowed.
*
* Let V be the set of nodes of the network and let W be an arbitrary
* non-empty proper subset of V. A cut associated with the subset W is
* a subset of all the edges, one node of which belongs to W and other
* node belongs to V \ W. The capacity of a cut (W, V \ W) is the sum
* of the capacities of all the edges, which belong to the cut. Minimal
* cut is a cut, whose capacity is minimal.
*
* On exit the routine stores flags of nodes v[i], i = 1, ..., nn, to
* locations cut[i], where cut[i] = 1 means that v[i] belongs to W and
* cut[i] = 0 means that v[i] belongs to V \ W, where W corresponds to
* the minimal cut found.
*
* RETURNS
*
* The routine returns the capacity of the min cut found. */
int min_cut(int nn, int ne, const int beg[/*1+ne*/],
const int end[/*1+ne*/], const cap[/*1+ne*/], int cut[/*1+nn*/])
{ int k;
/* sanity checks */
xassert(nn >= 2);
xassert(ne >= 0);
for (k = 1; k <= ne; k++)
{ xassert(1 <= beg[k] && beg[k] < end[k] && end[k] <= nn);
xassert(cap[k] > 0);
}
/* find min cut */
return min_cut_sw(nn, ne, beg, end, cap, cut);
}
/***********************************************************************
* NAME
*
* min_st_cut - find min (s,t)-cut for known max flow
*
* SYNOPSIS
*
* #include "mincut.h"
*
* DESCRIPTION
*
* This routine finds min (s,t)-cut in a given undirected network that
* corresponds to a known max flow from s to t in the network.
*
* Parameters nn, ne, beg, end, and cap specify the network in the same
* way as for the routine min_cut (see above).
*
* Parameters s and t specify, resp., the number of the source node
* and the number of the sink node, s != t, for which the min (s,t)-cut
* has to be found.
*
* Parameter x specifies the known max flow from s to t in the network,
* where locations x[1], ..., x[ne] contains elementary flow thru edges
* of the network (positive value of x[k] means that the elementary
* flow goes from node beg[k] to node end[k], and negative value means
* that the flow goes in opposite direction).
*
* This routine splits the set of nodes V of the network into two
* non-empty subsets V(s) and V(t) = V \ V(s), where the source node s
* belongs to V(s), the sink node t belongs to V(t), and all edges, one
* node of which belongs to V(s) and other one belongs to V(t), are
* saturated (that is, x[k] = +cap[k] or x[k] = -cap[k]).
*
* On exit the routine stores flags of the nodes v[i], i = 1, ..., nn,
* to locations cut[i], where cut[i] = 1 means that v[i] belongs to V(s)
* and cut[i] = 0 means that v[i] belongs to V(t) = V \ V(s).
*
* RETURNS
*
* The routine returns the capacity of min (s,t)-cut, which is the sum
* of the capacities of all the edges, which belong to the cut. (Note
* that due to theorem by Ford and Fulkerson this value is always equal
* to corresponding max flow.)
*
* ALGORITHM
*
* To determine the set V(s) the routine simply finds all nodes, which
* can be reached from the source node s via non-saturated edges. The
* set V(t) is determined as the complement V \ V(s). */
int min_st_cut(int nn, int ne, const int beg[/*1+ne*/],
const int end[/*1+ne*/], const int cap[/*1+ne*/], int s, int t,
const int x[/*1+ne*/], int cut[/*1+nn*/])
{ int i, j, k, p, q, temp, *head1, *next1, *head2, *next2, *list;
/* head1[i] points to the first edge with beg[k] = i
* next1[k] points to the next edge with the same beg[k]
* head2[i] points to the first edge with end[k] = i
* next2[k] points to the next edge with the same end[k] */
head1 = xalloc(1+nn, sizeof(int));
head2 = xalloc(1+nn, sizeof(int));
next1 = xalloc(1+ne, sizeof(int));
next2 = xalloc(1+ne, sizeof(int));
for (i = 1; i <= nn; i++)
head1[i] = head2[i] = 0;
for (k = 1; k <= ne; k++)
{ i = beg[k], next1[k] = head1[i], head1[i] = k;
j = end[k], next2[k] = head2[j], head2[j] = k;
}
/* on constructing the set V(s) list[1], ..., list[p-1] contain
* nodes, which can be reached from source node and have been
* visited, and list[p], ..., list[q] contain nodes, which can be
* reached from source node but havn't been visited yet */
list = xalloc(1+nn, sizeof(int));
for (i = 1; i <= nn; i++)
cut[i] = 0;
p = q = 1, list[1] = s, cut[s] = 1;
while (p <= q)
{ /* pick next node, which is reachable from the source node and
* has not visited yet, and visit it */
i = list[p++];
/* walk through edges with beg[k] = i */
for (k = head1[i]; k != 0; k = next1[k])
{ j = end[k];
xassert(beg[k] == i);
/* from v[i] we can reach v[j], if the elementary flow from
* v[i] to v[j] is non-saturated */
if (cut[j] == 0 && x[k] < +cap[k])
list[++q] = j, cut[j] = 1;
}
/* walk through edges with end[k] = i */
for (k = head2[i]; k != 0; k = next2[k])
{ j = beg[k];
xassert(end[k] == i);
/* from v[i] we can reach v[j], if the elementary flow from
* v[i] to v[j] is non-saturated */
if (cut[j] == 0 && x[k] > -cap[k])
list[++q] = j, cut[j] = 1;
}
}
/* sink cannot belong to V(s) */
xassert(!cut[t]);
/* free working arrays */
xfree(head1);
xfree(head2);
xfree(next1);
xfree(next2);
xfree(list);
/* compute capacity of the minimal (s,t)-cut found */
temp = 0;
for (k = 1; k <= ne; k++)
{ i = beg[k], j = end[k];
if (cut[i] && !cut[j] || !cut[i] && cut[j])
temp += cap[k];
}
/* return to the calling program */
return temp;
}
/***********************************************************************
* NAME
*
* min_cut_sw - find min cut with Stoer and Wagner algorithm
*
* SYNOPSIS
*
* #include "mincut.h"
* int min_cut_sw(int nn, int ne, const int beg[], const int end[],
* const cap[], int cut[]);
*
* DESCRIPTION
*
* This routine find min cut in a given undirected network with the
* algorithm proposed by Stoer and Wagner (see references below).
*
* Parameters of this routine have the same meaning as for the routine
* min_cut (see above).
*
* RETURNS
*
* The routine returns the capacity of the min cut found.
*
* ALGORITHM
*
* The basic idea of Stoer&Wagner algorithm is the following. Let G be
* a capacitated network, and G(s,t) be a network, in which the nodes s
* and t are merged into one new node, loops are deleted, but multiple
* edges are retained. It is obvious that a minimum cut in G is the
* minimum of two quantities: the minimum cut in G(s,t) and a minimum
* cut that separates s and t. This allows to find a minimum cut in the
* original network solving at most nn max flow problems.
*
* REFERENCES
*
* M. Stoer, F. Wagner. A Simple Min Cut Algorithm. Algorithms, ESA'94
* LNCS 855 (1994), pp. 141-47.
*
* J. Cheriyan, R. Ravi. Approximation Algorithms for Network Problems.
* Univ. of Waterloo (1998), p. 147. */
int min_cut_sw(int nn, int ne, const int beg[/*1+ne*/],
const int end[/*1+ne*/], const cap[/*1+ne*/], int cut[/*1+nn*/])
{ int i, j, k, min_cut, flow, temp, *head1, *next1, *head2, *next2;
int I, J, K, S, T, DEG, NV, NE, *HEAD, *NEXT, *NUMB, *BEG, *END,
*CAP, *X, *ADJ, *SUM, *CUT;
/* head1[i] points to the first edge with beg[k] = i
* next1[k] points to the next edge with the same beg[k]
* head2[i] points to the first edge with end[k] = i
* next2[k] points to the next edge with the same end[k] */
head1 = xalloc(1+nn, sizeof(int));
head2 = xalloc(1+nn, sizeof(int));
next1 = xalloc(1+ne, sizeof(int));
next2 = xalloc(1+ne, sizeof(int));
for (i = 1; i <= nn; i++)
head1[i] = head2[i] = 0;
for (k = 1; k <= ne; k++)
{ i = beg[k], next1[k] = head1[i], head1[i] = k;
j = end[k], next2[k] = head2[j], head2[j] = k;
}
/* an auxiliary network used in the algorithm is resulted from
* the original network by merging some nodes into one supernode;
* all variables and arrays related to this auxiliary network are
* denoted in CAPS */
/* HEAD[I] points to the first node of the original network that
* belongs to the I-th supernode
* NEXT[i] points to the next node of the original network that
* belongs to the same supernode as the i-th node
* NUMB[i] is a supernode, which the i-th node belongs to */
/* initially the auxiliary network is equivalent to the original
* network, i.e. each supernode consists of one node */
NV = nn;
HEAD = xalloc(1+nn, sizeof(int));
NEXT = xalloc(1+nn, sizeof(int));
NUMB = xalloc(1+nn, sizeof(int));
for (i = 1; i <= nn; i++)
HEAD[i] = i, NEXT[i] = 0, NUMB[i] = i;
/* number of edges in the auxiliary network is never greater than
* in the original one */
BEG = xalloc(1+ne, sizeof(int));
END = xalloc(1+ne, sizeof(int));
CAP = xalloc(1+ne, sizeof(int));
X = xalloc(1+ne, sizeof(int));
/* allocate some auxiliary arrays */
ADJ = xalloc(1+nn, sizeof(int));
SUM = xalloc(1+nn, sizeof(int));
CUT = xalloc(1+nn, sizeof(int));
/* currently no min cut is found so far */
min_cut = INT_MAX;
/* main loop starts here */
while (NV > 1)
{ /* build the set of edges of the auxiliary network */
NE = 0;
/* multiple edges are not allowed in the max flow algorithm,
* so we can replace each multiple edge, which is the result
* of merging nodes into supernodes, by a single edge, whose
* capacity is the sum of capacities of particular edges;
* these summary capacities will be stored in the array SUM */
for (I = 1; I <= NV; I++)
SUM[I] = 0.0;
for (I = 1; I <= NV; I++)
{ /* DEG is number of single edges, which connects I-th
* supernode and some J-th supernode, where I < J */
DEG = 0;
/* walk thru nodes that belong to I-th supernode */
for (i = HEAD[I]; i != 0; i = NEXT[i])
{ /* i-th node belongs to I-th supernode */
/* walk through edges with beg[k] = i */
for (k = head1[i]; k != 0; k = next1[k])
{ j = end[k];
/* j-th node belongs to J-th supernode */
J = NUMB[j];
/* ignore loops and edges with I > J */
if (I >= J)
continue;
/* add an edge that connects I-th and J-th supernodes
* (if not added yet) */
if (SUM[J] == 0.0)
ADJ[++DEG] = J;
/* sum up the capacity of the original edge */
xassert(cap[k] > 0.0);
SUM[J] += cap[k];
}
/* walk through edges with end[k] = i */
for (k = head2[i]; k != 0; k = next2[k])
{ j = beg[k];
/* j-th node belongs to J-th supernode */
J = NUMB[j];
/* ignore loops and edges with I > J */
if (I >= J)
continue;
/* add an edge that connects I-th and J-th supernodes
* (if not added yet) */
if (SUM[J] == 0.0)
ADJ[++DEG] = J;
/* sum up the capacity of the original edge */
xassert(cap[k] > 0.0);
SUM[J] += cap[k];
}
}
/* add single edges connecting I-th supernode with other
* supernodes to the auxiliary network; restore the array
* SUM for subsequent use */
for (K = 1; K <= DEG; K++)
{ NE++;
xassert(NE <= ne);
J = ADJ[K];
BEG[NE] = I, END[NE] = J, CAP[NE] = SUM[J];
SUM[J] = 0.0;
}
}
/* choose two arbitrary supernodes of the auxiliary network,
* one of which is the source and other is the sink */
S = 1, T = NV;
/* determine max flow from S to T */
flow = max_flow(NV, NE, BEG, END, CAP, S, T, X);
/* if the min cut that separates supernodes S and T is less
* than the currently known, remember it */
if (min_cut > flow)
{ min_cut = flow;
/* find min (s,t)-cut in the auxiliary network */
temp = min_st_cut(NV, NE, BEG, END, CAP, S, T, X, CUT);
/* (Ford and Fulkerson insist on this) */
xassert(flow == temp);
/* build corresponding min cut in the original network */
for (i = 1; i <= nn; i++) cut[i] = CUT[NUMB[i]];
/* if the min cut capacity is zero (i.e. the network has
* unconnected components), the search can be prematurely
* terminated */
if (min_cut == 0)
break;
}
/* now merge all nodes of the original network, which belong
* to the supernodes S and T, into one new supernode; this is
* attained by carrying all nodes from T to S (for the sake of
* convenience T should be the last supernode) */
xassert(T == NV);
/* assign new references to nodes from T */
for (i = HEAD[T]; i != 0; i = NEXT[i])
NUMB[i] = S;
/* find last entry in the node list of S */
i = HEAD[S];
xassert(i != 0);
while (NEXT[i] != 0)
i = NEXT[i];
/* and attach to it the node list of T */
NEXT[i] = HEAD[T];
/* decrease number of nodes in the auxiliary network */
NV--;
}
/* free working arrays */
xfree(HEAD);
xfree(NEXT);
xfree(NUMB);
xfree(BEG);
xfree(END);
xfree(CAP);
xfree(X);
xfree(ADJ);
xfree(SUM);
xfree(CUT);
xfree(head1);
xfree(head2);
xfree(next1);
xfree(next2);
/* return to the calling program */
return min_cut;
}
/* eof */