【网络流】最大流问题Edmonds-Karp算法

       实现最大流有好几种算法,比如Dinic或者ISAP算法,Edmonds-Karp只是其中最好理解的一种算法,它的实现要运用到增广路与BFS,当然也可以用DFS,但效率太低。网络流这东西是用来求从s点到t点(起点为s,终点为t)的流量问题,因为类似网络数据传输,所以叫做网络流。

        最大流说简单点就是使从s到t的流量最大。这玩意儿需要注意三个事实(起点为s,终点为t):

【网络流】最大流问题Edmonds-Karp算法_第1张图片

        BFS是普及组必须掌握的,就不多讲了。以下是一些基本知识(有些东西在后面会简写,所以注意一下):

        1.容量(c),每条边最大运输量。

        2.流量(f),当前用了的运输量。

        3.残量网络,即每一条路上容量与流量之差,必须为正数,注意,若a->b的c为16,f为10,在残量网络中a->b为6还有一条边,b->a为10,有两条边!因为可以当做b->a为容量0,流量-11。例如图(a)的残量网络为图(b)(前一个数为流量,后一个数为容量)。

【网络流】最大流问题Edmonds-Karp算法_第2张图片

        4.增广路,每次在图中找到一条满足上述基本事实的一条从s到t的路(这里用BFS找),这条路上贡献的流量就是其最小残量,每次找到一条路,就让答案加上最小残量,当没有增广路时,可以证明答案即为最优。

        下面贴上代码:

#include
#include
#include
#include
#define maxn 1005
#define INF 2147483647
using namespace std;
int read(){
	int ret=0,f=1;char  ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-')f=-f;ch=getchar();}
	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
struct Edge{//用来维护每条边的信息 
	int from,to,cap,flow;
	Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct EdmondsKarp{
	int n,m,s,t;//n:点数,m:边数,s:起点,t:终点 
	vector edges;//边数开两倍(还有反向网络),这里偷一下懒 
	vector G[maxn];//领接表,G[i][j]表示点i到延伸出去的第j条边在edges中的位置 
	int a[maxn];//最小残量,用以增广 
	int p[maxn];//p[i]表示到达节点i的那条边的编号,因为每次增广只增广一条路径,所以不用开二维 
	void init(){//不多说,初始化 
		n=read();m=read();s=read();t=read();
		for(int i=0;i Q;//先进先出的队列 
			Q.push(s);
			a[s]=INF;
			while(!Q.empty()){//里面就是在BFS 
				int x=Q.front();//取队首 
				Q.pop();
				for(int i=0;ie.flow){
						p[e.to]=G[x][i];//记住回去的路 
						a[e.to]=min(a[x],e.cap-e.flow);//去最小残量 
						Q.push(e.to);
					}
				}
				if(a[t]) break;//此条路已到达t 
			} 
			if(!a[t]) break;//没有增广路时,即为最大流 
			for(int u=t;u!=s;u=edges[p[u]].from){//借助p数组倒回去给此次找到的增广路上的每条路的流量都加上最小残量a[t] 
				edges[p[u]].flow+=a[t];
				edges[p[u]^1].flow-=a[t];//反向网络也要减掉a[t] 
			}
			ret+=a[t];//答案加上最小残量a[t] 
		}
		return ret;
	}
}ans;
int main(){
	freopen("ls.in","r",stdin);
	ans.init();
	printf("%d",ans.Maxflow());
	return 0;
}

感谢《算法竞赛 入门经典》的图

你可能感兴趣的:(网络流,图论)