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

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

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


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

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

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

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


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

        下面贴上代码:

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#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<Edge> edges;//边数开两倍(还有反向网络),这里偷一下懒 
	vector<int> 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<n;i++) G[i].clear();
		edges.clear();
		for(int i=1;i<=m;i++){
			int from=read(),to=read(),cap=read();
			edges.push_back(Edge(from,to,cap,0));
			edges.push_back(Edge(to,from,0,0));//反向网络 
			int k=edges.size();
			G[from].push_back(k-2);
			G[from].push_back(k-1);  
		}
	}
	int Maxflow(){
		int ret=0;
		while(1){
			memset(a,0,sizeof(a));
			queue<int> Q;//先进先出的队列 
			Q.push(s);
			a[s]=INF;
			while(!Q.empty()){//里面就是在BFS 
				int x=Q.front();//取队首 
				Q.pop();
				for(int i=0;i<G[x].size();i++){
					Edge& e=edges[G[x][i]];
					if(!a[e.to]/*判环*/&&e.cap>e.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;
}

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

猜你喜欢

转载自blog.csdn.net/weixin_41811117/article/details/79677054