讲解
对于求一个图起点到终点的最大网络流。
我们可以利用增广路来求。
将图的容量初始化,注意:读入边的信息时,必须只能单向读入,这是为了更好的增加残余网络的反向弧。
对图所有的流量均初始化为0。
首先我们应该寻找增广路,对于寻找一条增广路,我们可以这样做:
- 第一步:我们首先通过广度搜索或者深度搜索来求出这个图的其中一条路径。并用Pre数组记录前驱。
- 第二步:我们可以通过求出此路径的残余网络的最大瓶颈,其实其中一条边残余网络的瓶颈值就是路径的限制容量-当前的流量。
- 第三步:将整条路径增加流量。但是在增加流量的同时,我们需要反向添加其容量,所增加的值为第二步求得的瓶颈值。为何要增加反向弧,因为对于一个图,我们增加了其正边流量时就相当于减少了其正边的剩余容量,而反向的容量恰好会增加正边的容量的减少值,例如,张三欠了李四10元,而李四不让张三还10元,相当于张三增加了10元,此与原理相似。所以,利用反向弧定理,我们可以求出更多的增广路,求出更大的流量。
通过以上三步,我们就寻找到了一条增广路。
按照此算法循环下去,我们可以找到很多条增广路,且每找到一条增广路的同时,其两点间的网络流量会增加。这样,若找不到任意一条增广路,则容易证明当前的网络流量最大从而求出最大网络流。
对于求起点到终点的流量,我们一般会想到将终点汇聚的流量求和,但是我们知道,从起点流出去的流量之和是等于终点汇聚的流量的之和,利用这样的性质我们可以通过把对起点流出去的流量来求和从而求出最大网络流。
代码
下面是最大网络流增广路算法的代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int f[210][210],c[210][210],pre[210];
bool flag=true,b[210];
int n,m,h,t,que[200*200+10],ans;
int main(){
cin>>m>>n;
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
c[u][v]=w;
//输入边,注意,只能单向读入。
}
flag=true;
//将其作为一个标记,若其值为假,则找不到增广路。
while(flag){
flag=false;
memset(b,false,sizeof(b));
h=0;
t=1;
que[1]=1;
b[1]=true;
//第一步,寻找一条路径作为增广路。
while(h<t){
h++;
int p=que[h];
for(int i=1;i<=n;i++)
if(!b[i] && f[p][i]<c[p][i]){
que[++t]=i;
pre[i]=p;
b[i]=true;
if(i==n){
flag=true;
//找到一条增广路,将flag设为真。
break;
}
}
if(flag)
break;
}
if(!flag)
break;
//没找到增广路,直接跳出循环。
//求瓶颈的值。
int k=n,pj=0xfffffff;
while(pre[k]){
int u=pre[k];
pj=min(pj,c[u][k]-f[u][k]);
k=u;
}
k=n;
//添加残余网络流量和反向弧。
while(pre[k]){
int u=pre[k];
f[u][k]+=pj;
c[k][u]+=pj;
k=u;
}
}
//对流出流量求和。
for(int i=2;i<=n;i++){
ans+=f[1][i];
}
cout<<ans<<endl;
}
由于网络流的性质,我们还可以求二分图的最大匹配。
但是由于其耗时为O(n^3),我们还是最好用匈牙利算法来求二分图的最大匹配,且用此算法在求最大网络流时只需要用邻接矩阵储存信息即可。