流网络算法

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=vVf(s,v)vVf(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)=vVf(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, 有两个变化,

  1. 原来的边e如果存在,e将作为逆向边存在于Ef中,
  2. 同时增加一条边,该边的容量为原始容量减去流的大小

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算法及代码实现

在这里插入图片描述

  1. 将每条边上的流值f初始化为0
  2. 如果在残存网络中存在一条增广路径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描述一致。

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodePanda@GPF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值