网络流算法

本文介绍了最大流算法的基本概念,包括通过不断寻找增广路径来逐步逼近最大流的过程,并详细对比了三种不同算法:DFS、Edmonds-Karp最短增广路算法及Dinic快速网络流算法的特点与实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求最大流的过程,就是不断找到一条源到汇的路径,然后构建残余网络,

再在残余网络上寻找新的路径,使总流量增加,然后形成新的残余网络,

再寻找新路径…..直到某个残余网络上找不到从源到汇的路径为止,最

大流就算出来了。


每次寻找新流量并构造新残余网络的过程,就叫做寻找流量的“增广路径”,也叫“增广”


各种算法的不同之处,也就是在寻找一条从源到汇的过程中进行优化而已

1) DFS 

直接用dfs寻找增广路径,直到找不到路

这种算法十分简单,但复杂度高


本来2次就行了,但有可能要200次


2)Edmonds-Karp 最短增广路算法(BFS)  O(n*m*m)  (n是点数,m是边数)

利用bfs寻找一条增广路径

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=300;
const int INF=(1<<30);
int G[maxn][maxn];
int prev[maxn];    //记录前驱节点,记录增广路径 
bool vis[maxn]; 
int Augment(int s,int t,int m)      //增广 
{
	queue<int> Q;
	bool isFound=false;  //标记是否找到了增广路 
	memset(vis,false,sizeof(vis));
	memset(prev,0,sizeof(prev));
	
	Q.push(s);
	vis[s]=true;
	while(!Q.empty())
	{
		int u=Q.front(); Q.pop();
		if(u==t) {    //找到增广路径了 
			isFound=true;
			break;
		}
		for(int v=1;v<=m;v++)     //注意是从1开始计数的 
		if(G[u][v]>0&&!vis[v]){
			prev[v]=u;
			vis[v]=1;
			Q.push(v); 
		}
	}
		
	if(!isFound) return 0;   //无路可走 
	int MinFlow=INF;   

	int u=m;
	while(u!=s){    //计算途中最小流 
		MinFlow=min(MinFlow,G[prev[u]][u]);
		u=prev[u];
	}
	
	u=m;
	while(u!=s){
		G[prev[u]][u]-=MinFlow;  //更新途径路的流量 
		G[u][prev[u]]+=MinFlow;  //加上反向边 
		u=prev[u];
	}
	return MinFlow;
}
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		int u,v,w;
		memset(G,0,sizeof(G));
		for(int i=0;i<n;i++){
			cin>>u>>v>>w;   //u-->v 上限是w 
			G[u][v]+=w;    //可能有多条边 
		}
		int MaxFlow=0;
		int aug;
		while(aug=Augment(1,m,m))  //(start,terminal,Node's number)
			MaxFlow+=aug;     //加上每次增广的流 
		cout<<MaxFlow<<endl; 
	}
	return 0;	
} 




3)Dinic 快速网络流算法(BFS+DFS)   O(n*n*m) (n是点数,m是边数)

因为上述算法都是找到一条从源到汇的路径并进行增广后,重新回到源点继续寻找路径

但Dinic并不是从头找

Dinic优点如下:

1)结合BFS快速的优点,先用BFS将源点到汇点分层,每次DFS时,只能走到下一层结点

2)因为每次增广后增广路径上至少有一条路径流量上限为0,所以直接返回到增广路径中流量为0的起点进行增广,如果有多条增广路径的流量为0,就选择最上面那一条

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=300;
const int INF=(1<<30);
int m,n;   
int G[maxn][maxn];
bool vis[maxn]; 
int layer[maxn];    //记录层次 
bool getLayer(int s,int t,int m)    //分层 
{
	queue<int> Q;
	memset(layer,0,sizeof(layer));
	
	layer[s]=1;
	Q.push(s);
	while(!Q.empty())
	{
		int u=Q.front(); Q.pop();
		for(int v=1;v<=m;v++)
		if(G[u][v]>0&&!layer[v]){
			layer[v]=layer[u]+1;
			if(v==t) return true;  //一旦到终点m就不必分层了 
			Q.push(v);
		}	
	}
	return false;
}
int Dinic(int s,int t,int m)
{
	int MaxFlow=0;
	while(getLayer(s,t,m))
	{
		deque<int> DQ;   //保存路径 
		memset(vis,0,sizeof(vis));
		vis[s]=1;
		DQ.push_back(1);
		while(!DQ.empty())
		{
			int u=DQ.back();
			if(u==t)          //找到增广路径了,便找出最小流,更新网络流,添加反向边 
			{
				int MinFlow=INF;  //最小流 
				int pMin;         //最小流的上一个节点位置 
				for(int i=1;i<DQ.size();i++)
				{
					int u=DQ[i-1];
					int v=DQ[i];
					if(G[u][v]>0&&MinFlow>G[u][v]) //找出最小流 
					{
						MinFlow=G[u][v];
						pMin=u; 
					} 
				}
				MaxFlow+=MinFlow;   //累加到最大流 
				for(int i=1;i<DQ.size();i++)
				{
					int u=DQ[i-1];
					int v=DQ[i];
					G[u][v]-=MinFlow; //更新最小流 
					G[v][u]+=MinFlow; //添加反向边 
				}
				
				while(!DQ.empty()&&DQ.back()!=pMin){  //退栈到PMin,回溯继续找增广路径 
					vis[DQ.back()]=0;
					DQ.pop_back();
				}
				continue;
			}
			int ok=0;
			for(int v=1;v<=m;v++)
			if(G[u][v]>0&&!vis[v]&&layer[v]==layer[u]+1)  //只能走到下一层 
			{
				ok=1;
				vis[v]=1;
				DQ.push_back(v);
				break;	
			}
			
			if(!ok) DQ.pop_back();  //不能走就pop,换一条路走	
		} 
	}
	return MaxFlow; 
}
int main()
{
	while(cin>>n>>m)   //从1~m的最大流 //m为顶点数,n为边数 
	{
		int u,v,w;
		memset(G,0,sizeof(G));
		for(int i=0;i<n;i++){
			cin>>u>>v>>w;     //u-->v  上限是w 
			G[u][v]+=w;       //可能有多条边 
		}
		cout<<Dinic(1,m,m)<<endl; 
	}
	return 0;	
} 







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值