1. 流网络定义
2. 流的两个性质
其中 f ( u , v ) f(u,v) f(u,v)表示从节点u到节点v的一个流
∣ f ∣ = ∑ v ∈ V f ( s , v ) − ∑ v ∈ V f ( v , s ) |f|=\sum_{v\in V}f(s,v)-\sum_{v\in V}f(v,s) ∣f∣=v∈V∑f(s,v)−v∈V∑f(v,s),即流f的值是从源节点流出的总流量减去流入源节点的总流量
最大流问题就是求f的最大值,即源点s的流出值减去流入值最大
上图的这种配置的流的值是4,对于一幅有向图G,在满足流的两个条件的情况下,如何配置各个边上的流大小,使得f的值最大,这就是最大流问题
3. 流的切分
流的切分是指将G中的源点s和汇点t放置于两个不同的集合中
定理1:任意一个流切分中的
f
(
S
,
T
)
f(S,T)
f(S,T)的值就等于G中流值,即
f
(
S
,
T
)
=
∑
v
∈
V
f
(
s
,
v
)
=
∣
f
∣
f(S,T)=\sum_{v\in V}f(s,v)=|f|
f(S,T)=∑v∈Vf(s,v)=∣f∣
定理2:G中流值f的上界是切分中的容量, 即
∣
f
∣
<
=
c
(
S
,
T
)
|f|<=c(S,T)
∣f∣<=c(S,T)
解释一下
c
(
S
,
T
)
c(S,T)
c(S,T):
4. 残存网络
原图E到残差图Ef, 有两个变化,
- 原来的边e如果存在,e将作为逆向边存在于Ef中,
- 同时增加一条边,该边的容量为原始容量减去流的大小
5. 增广路径与残存容量
上面的增广路径上面的每条边的容量即为残存容量,增广路径上的残存容量的最小值即为增广路径p的的残存容量
c f ( p ) = m i n ( c f ( u , v ) : ( u , v ) 属 于 路 径 p ) c_f(p)=min(c_f(u,v):(u,v)属于路径p) cf(p)=min(cf(u,v):(u,v)属于路径p)
6. 最大流与最小切割
当图G可以取得最大流值f时,对应着最小切分(切分概念在第3小节简述过)
即1==>2, 2==>3, 3==>1(条件1可以推出条件2,条件2可以推出条件3,条件3可以推出条件1)
7. Ford-Fulkerson算法及代码实现
- 将每条边上的流值f初始化为0
- 如果在残存网络中存在一条增广路径p,然后使用残存容量对增广路径上的的边上面的流值进行增加,当残存边是原来网络中的一条边时,增加流量;否则,缩减流量;重复过程,直到不存在增广路径为止
完整代码及实现:
package maxstream;
public class FlowEdge {
int from;//边的起点
int to;//边的终点
double capacity;//边的容量
double flow;//边的流量
public FlowEdge(int from, int to, double capacity, double flow) {
super();
this.from = from;
this.to = to;
this.capacity = capacity;
this.flow = 0.0;
}
public int other(int vertex) {
return from==vertex?to:from;//返回vertex所在边的另一个顶点
}
public double residualCapacity(int vertex) {
//对于某条边e(初始时加入的边 实际上残余网络中的逆向边并没有实际生成 但是可以使用计算得到的残存容量来模拟)
if(vertex==from) {//某条边:e(from-to) 现在如果存在逆向边to->from 那么残存容量就是flow
return flow;
}else if(vertex==to) {//原始某条边:e(from-to) 现在e作为新加的残存边 残存容量为capacity-flow
return capacity-flow;
}else {
throw new RuntimeException("Inconsistent Edge");
}
}
/*
* 给某条边流值添加最小残存容量
* from->to(8/12)
* from->to 4 to->from 8
*
*/
public void addResidualFlowTo(int vertex,double delta) {
if(vertex==from) {//给边to->from增加流量等价于给边from->to减少流值
flow-=delta;
}else if(vertex==to) {//给边from->to增加流值
flow+=delta;
}else {
throw new RuntimeException("Inconsistent Edge");
}
}
public String toString() {
return String.format("%d->%d %.2f %.2f", from,to,capacity,flow);
}
}
package maxstream;
import java.util.ArrayList;
public class FlowNetwork {
public int V;
public int E;
ArrayList<FlowEdge>[] adj;
public FlowNetwork(int v) {
this.V=v;
this.E=0;
adj=new ArrayList[V];
for(int i=0;i<V;i++) {
adj[i]=new ArrayList<>();
}
}
/*
* 在流网络中添加边
*/
public void addEdge(FlowEdge e) {
int v = e.from;
int w = e.to;
//System.out.println("v="+v+" to="+w);
validateVertex(v);
validateVertex(w);
adj[v].add(e);
adj[w].add(e);
E++;
}
private void validateVertex(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
}
package maxstream;
import java.util.LinkedList;
public class FordFulkerson {
boolean[] marked;//残余网络中是否存在s->v的路径
FlowEdge[] edgeTo;//s->路径上的最后一条边
double value;//当前最大流量
public FordFulkerson(FlowNetwork G,int s,int t) {
while(hasAugementingPath(G, s, t)) {
double minCapacity=Double.POSITIVE_INFINITY;//当前增广路径上的最小剩余容量
//从汇点出发往回走,直到走到源点s 因为edgeTo[v]表示达到节点v的最后一条边 需要往前回溯
for(int v=t;v!=s;v=edgeTo[v].other(v)) {
minCapacity=Math.min(minCapacity,edgeTo[v].residualCapacity(v));
}
//给增广路径上的边增大流值
for(int v=t;v!=s;v=edgeTo[v].other(v)) {
edgeTo[v].addResidualFlowTo(v, minCapacity);
}
value+=minCapacity;//最终的最大流值累加增广路径上的最小残余容量
}
}
/*
* 判断是否存在一条s->t的增广路径
*/
public boolean hasAugementingPath(FlowNetwork G,int s,int t) {
marked=new boolean[G.V];
edgeTo=new FlowEdge[G.V];
LinkedList<Integer> q=new LinkedList<Integer>();
marked[s]=true;
q.offer(s);//源点入队
while(!q.isEmpty()) {
int v=q.poll();
for(FlowEdge e:G.adj[v]) {
int w=e.other(v);//w是v所在边的另外一个顶点
//w未被访问 并且 v->w 或者 w->v上面剩余容量大于0 说明可以前进
if(e.residualCapacity(w)>0&&!marked[w]) {
edgeTo[w]=e;//保存路径上的最后一条边
marked[w]=true;//标记w
q.offer(w);//w入队
}
}
}
return marked[t];//t可以到达说明存在增广路径
}
public static void main(String[] args) {
FlowNetwork G=new FlowNetwork(6);//创建含有6个顶点的流网络
int s=0,t=G.V-1;
G.addEdge(new FlowEdge(0, 1, 16.0, 0.0));
G.addEdge(new FlowEdge(0, 2, 13.0, 0.0));
G.addEdge(new FlowEdge(1, 3, 12.0, 0.0));
G.addEdge(new FlowEdge(2, 1, 4.0, 0.0));
G.addEdge(new FlowEdge(2, 4, 14.0, 0.0));
G.addEdge(new FlowEdge(3, 2, 9.0, 0.0));
G.addEdge(new FlowEdge(3, 5, 20.0, 0.0));
G.addEdge(new FlowEdge(4, 3, 7.0, 0.0));
G.addEdge(new FlowEdge(4, 5, 4.0, 0.0));
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) {
System.out.println(e);
}
}
}
System.out.println("\nMax flow value: "+maxFlow.value);
}
}
运行结果和图片f描述一致。