演算法#18--最大流量問題(網路流演算法)
阿新 • • 發佈:2019-01-03
1.物理模型
請想象一組相互連線大小不一的輸油管道,每根管道有它自己的流量和容量,問從起點到終點的最大流量是多少?如下流量圖中,深色路徑流量之和為最大路徑。如何求得,下面內容將詳細介紹。
2.數學模型
一個流量網路,是一張邊的權重(這裡稱為容量)為正的加權有向圖。一個st-流量網路有兩個已知的頂點,即起點s和終點t。
3.Ford-Fulkerson演算法
也稱為增廣路徑演算法。它的定義是:網路中的初始流量為零,沿著任意從起點到終點(且不含有飽和的正向邊或是空逆向邊)的增廣路徑增大流量,直到網路中不存在這樣的路徑為止。
也即,假設x為該路徑上的所有邊中未使用容量的最小值,那麼只需將所有邊的流量增大x,即可將網路中的總流量至少增大x。反覆這個過程,直到所有起點到終點的路徑上至少有一條邊是飽和的。
4.剩餘網路
這裡,與流量對應的邊的方向和流量本身相反。程式碼如下FlowNetwork類。
5.程式碼實現
對Ford-Fulkerson演算法最簡單的實現可能就是最短增廣路徑演算法了(最短指的是路徑長度最小,而非流量或是容量)。增廣路徑的查詢等價於剩餘網路中的廣度優先搜尋(BFS)。
public class FordFulkerson
{
private boolean[] marked; //在剩餘網路中是否存在從s到v的路徑
private FlowEdge[] edgeTo; //從s到v的最短路徑上的最後一條邊
private double value; //當前最大流量
public FordFulkerson(FlowNetwork G, int s, int t)
{ //找出從s到t的流量網路G的最大流量配置
while(hasAugmentingPath(G, s, t))
{ //利用所有存在的增廣路徑
//計算當前的瓶頸容量
double bottle = Double.POSITIVE_INFINITY;
for(int v = t; v != s; v = edgeTo[v].other(v))
{
bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
}
//增大流量
for(int v = t; v != s; v = edgeTo[v].other(v))
{
edgeTo[v].addResidualFlowTo(v, bottle);
}
value += bottle;
}
}
private boolean hasAugmentingPath(FlowNetwork G, int s, int t)
{
marked = new boolean[G.V()]; //標記路徑已知的頂點
edgeTo = new FlowEdge[G.V()]; //路徑上的最後一條邊
Queue<Integer> q = new Queue<Integer>();
marked[s] = true; //標記起點
q.enqueue(s); //並將它入列
while(!q.isEmpty())
{
int v = q.dequeue();
for(FlowEdge e : G.adj(v))
{
int w = e.other(v);
if(e.residualCapacityTo(w) > 0 && !marked[w])
{//(在剩餘網路中)對於任意一條連線到一個未標記的頂點的邊
edgeTo[w] = e; //保持路徑上的最後一條邊
marked[w] = true; //標記w,因為路徑現在是已知的了
q.enqueue(w); //將它入列
}
}
}
return marked[t];
}
public double value()
{
return value;
}
public boolean inCut(int v)
{
return marked[v];
}
public static void main(String[] args)
{
FlowNetwork G = new FlowNetwork(6);
int[] from = new int[]{0, 0, 1, 1, 2, 2, 3, 4};
int[] to = new int[]{1, 2, 3, 4, 3, 4, 5, 5};
double[] capacity = new double[]{2.0, 3.0, 3.0, 1.0, 1.0, 1.0, 2.0, 3.0};
for(int i = 0; i < from.length; i++)
{
FlowEdge edge = new FlowEdge(from[i], to[i], capacity[i]);
G.addEdge(edge);
}
int s = 0, t = G.V() - 1;
FordFulkerson maxflow = new FordFulkerson(G, s, t);
System.out.println("Max flow from " + s + " to " + t);
for(int v = 0; v < G.V(); v++)
{
for(FlowEdge e : G.adj(v))
{
if(v == e.from() && e.flow() > 0)
{
System.out.println(" " + e);
}
}
}
System.out.println("Max flow value = " + maxflow.value());
}
}
輸出:
Max flow from 0 to 5
0->2 3.00 2.00
0->1 2.00 2.00
1->4 1.00 1.00
1->3 3.00 1.00
2->4 1.00 1.00
2->3 1.00 1.00
3->5 2.00 2.00
4->5 3.00 2.00
Max flow value = 4.0
public class FlowNetwork
{
private final int V;
private int E;
private Bag<FlowEdge>[] adj;
@SuppressWarnings("unchecked")
public FlowNetwork(int V)
{
this.V = V;
this.E = 0;
adj = (Bag<FlowEdge>[]) new Bag[V];
for(int v = 0; v < V; v++)
{
adj[v] = new Bag<FlowEdge>();
}
}
public int V()
{
return V;
}
public int E()
{
return E;
}
public void addEdge(FlowEdge e)
{
int v = e.either(), w = e.other(v);
adj[v].add(e);
adj[w].add(e);
E++;
}
public Iterable<FlowEdge> adj(int v)
{
return adj[v];
}
public Iterable<FlowEdge> edges()
{
Bag<FlowEdge> b = new Bag<FlowEdge>();
for(int v = 0; v < V; v++)
{
for(FlowEdge e : adj[v])
{
if(e.other(v) > v)
{
b.add(e);
}
}
}
return b;
}
}
public class FlowEdge
{
private final int v;
private final int w;
private final double capacity;
private double flow;
public FlowEdge(int v, int w, double capacity)
{
this.v = v;
this.w = w;
this.capacity = capacity;
this.flow = 0;
}
public int from()
{
return v;
}
public int to()
{
return w;
}
public double capacity()
{
return capacity;
}
public double flow()
{
return flow;
}
public int either()
{
return v;
}
public int other(int vertex)
{
if(vertex == v)
{
return w;
}
else if(vertex == w)
{
return v;
}
else
{
throw new RuntimeException("Inconsistent edge");
}
}
public double residualCapacityTo(int vertex)
{
if(vertex == v)
{
return flow;
}
else if(vertex == w)
{
return capacity - flow;
}
else
{
throw new RuntimeException("Inconsistent edge");
}
}
public void addResidualFlowTo(int vertex, double delta)
{
if(vertex == v)
{
flow -= delta;
}
else if(vertex == w)
{
flow += delta;
}
else
{
throw new RuntimeException("Inconsistent edge");
}
}
public String toString()
{
return String.format("%d->%d %.2f %.2f", v, w, capacity, flow);
}
}