这是我参与「第四届青训营 」笔记创作活动的第3天
Edmonds–Karp Algorithm
这节课简单介绍 Edmonds-Karp 算法,由 Edmonds 和 Karp 两人在 1972 年提出。Edmonds-Karp 算法是 Ford-Fulkerson 算法的一种特例,一定能找到最大流。Edmonds-Karp 算法的时间复杂度低于 Ford-Fulkerson 算法。 课件: github.com/wangshusen/… 参考文献: 1. L. R. Ford and D. R. Fulkerson. Maximal flow through a network. Canadian Journal of Mathematics, 8: 399–404, 1956. 2. J. Edmonds and R. M. Karp. Theoretical improvements in algorithmic efficiency for network flow problems. Journal of the ACM. 19 (2): 248–264, 1972.
这是我做的第⼀道最⼤流的题 HDU1532(最⼤流EK算法模板题) ,我这题是⽤EK算法写的,EK算法最关键的地⽅在于反向边的建⽴,这⾥也不好理解,为什么要建⽴反向边。⾸先我们假设没有反向边的情况,如有⼀条路径从源点到汇点,那么这条路径上我们取所有边的最⼩值,然后让这个最⼩值的流从源点流到汇点,这样⼀定是能够满⾜所有边的要求(因为我们找的是这条路径上最⼩的),这样我们就增加了从源点到汇点的流,但是现在我们要求⼀个最⼤流,所以我们希望有更多这样的路径,所以当我们找完所有的路径之后,得到的流就是最⼤流。但是,我们找每条路径时,每次都是随机找⼀条路径,所以会导致⼀个情况,就是找了这条路径之后,会对后⾯的路径有影响,导致最后得到的流的和并不是最⼤,在这⾥我画了⼀个图帮助理解。⽐如这个图,边的权值都为1
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int dp[100][100],pre[100];
const int tmin=999999999;
int maxflow;
void EK(int start,int end,int n){
while(1){
queue<int>q;
q.push(1);//源点为1,进队
int minflow=tmin;
memset(pre,0,sizeof(pre));//初始化增广路径数组,题目中的顶点是从1开始的
while(!q.empty()){//bfs找增广路
int u=q.front();
q.pop();
for(int i=1;i<=n;i++){
if(dp[u][i]>0&&!pre[i]){//pre[i]除了记录当前顶点的父亲,还记录当前顶点有没被访问过
pre[i]=u;
q.push(i);
}
}
}
if(pre[end]==0)//顶点的父亲为空,表示找不到增广路,很容易理解吧。。
break;
for(int i=end;i!=start;i=pre[i]){//找出增广路中最小残余量
minflow=min(dp[pre[i]][i],minflow);
}
for(int i=end;i!=start;i=pre[i]){//更新增广路中正反向弧的流量
dp[pre[i]][i]-=minflow;
dp[i][pre[i]]+=minflow;
}
maxflow+=minflow;
}
}
int main(){
int count=0;
int n,m;
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
count++;
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
dp[u][v]+=w;
}
maxflow=0;
EK(1,n,n);
printf("Case %d: %d\n",count,maxflow);
}
return 0;
}