网络流算法
问题描述:对应下图流量图的输油网络,只有一个入口和一个出口,每条管道都有相应的容量,中间每个节点的流入量和流出量要相同,怎样设计流量网络可以使出口的流量最大。
流量图可以很自然的转换成有向图,每条边有容量限制和当前的流量,示意图如下:
FordFulkerson算法也叫做增广路径算法可以解决最大流问题,该算法的思路是寻找一条从起点s到终点t的路径,该路径每条边的剩余流量不能为0,找出每条边剩余流量的最小值bottle,把每条边的流程增加bottle,反复此过程直到找不出这样的路径为止。这样的路径也叫做增广路径。
增广路径中边的方向也可以与流量的方向相反,这条边中的流量不能为空,正向边是流量增加bottle,方向边是流量减少bottle,如下图的0-2-3-1-4-5路径,。
增广路径中从起点指出的边和指向终点的边必然是正向的。
算法实现如下:,示意图如下:
采用广度优先搜索寻找增广路径,所以也叫做最短增广路径
public class FordFulkerson {
private static final double FLOATING_POINT_EPSILON = 1E-11;
private final int V; // number of vertices
private boolean[] marked; // marked[v] = true iff s->v path in residual graph
private FlowEdge[] edgeTo; // edgeTo[v] = last edge on shortest residual s->v path
private double value; // current value of max flow
/**
* Compute a maximum flow and minimum cut in the network {@code G}
* from vertex {@code s} to vertex {@code t}.
*
* @param G the flow network
* @param s the source vertex
* @param t the sink vertex
* @throws IllegalArgumentException unless {@code 0 <= s < V}
* @throws IllegalArgumentException unless {@code 0 <= t < V}
* @throws IllegalArgumentException if {@code s == t}
* @throws IllegalArgumentException if initial flow is infeasible
*/
public FordFulkerson(FlowNetwork G, int s, int t) {
V = G.V();
validate(s);
validate(t);
if (s == t) throw new IllegalArgumentException("Source equals sink");
if (!isFeasible(G, s, t)) throw new IllegalArgumentException("Initial flow is infeasible");
// while there exists an augmenting path, use it
value = excess(G, t);
while (hasAugmentingPath(G, s, t)) {
// compute bottleneck capacity
double bottle = Double.POSITIVE_INFINITY;
for (int v = t; v != s; v = edgeTo[v].other(v)) {
bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
}
// augment flow
for (int v = t; v != s; v = edgeTo[v].other(v)) {
edgeTo[v].addResidualFlowTo(v, bottle);
}
value += bottle;
}
// check optimality conditions
assert check(G, s, t);
}
/**
* Returns the value of the maximum flow.
*
* @return the value of the maximum flow
*/
public double value() {
return value;
}
/**
* Returns true if the specified vertex is on the {@code s} side of the mincut.
*
* @param v vertex
* @return {@code true} if vertex {@code v} is on the {@code s} side of the micut;
* {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean inCut(int v) {
validate(v);
return marked[v];
}
// throw an IllegalArgumentException if v is outside prescibed range
private void validate(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
// is there an augmenting path?
// if so, upon termination edgeTo[] will contain a parent-link representation of such a path
// this implementation finds a shortest augmenting path (fewest number of edges),
// which performs well both in theory and in practice
//广度优先搜索寻找增广路径
private boolean hasAugmentingPath(FlowNetwork G, int s, int t) {
edgeTo = new FlowEdge[G.V()];
marked = new boolean[G.V()];
// breadth-first search
Queue<Integer> queue = new Queue<Integer>();
queue.enqueue(s);
marked[s] = true;
while (!queue.isEmpty() && !marked[t]) {
int v = queue.dequeue();
for (FlowEdge e : G.adj(v)) {
int w = e.other(v);