网络流之最大流(FF)——杨子曰算法
先上题:在一个国家里,有很多下水道(有向边),下水道都有自己能运输水的最大量,这些下水道把所有的n个城市都连在了一起,现在要从拥有无穷无尽水源的城市通过下水道,输送到城市n(除城市1外其他城市都没有水),问,最多能运输多少水?
今天,我们来曰图论中一个高大上的算法——网络流——中的最大流。就是在一个有向图中,水流从一个点流向另一个点,能流过去的水流量。
解释下概念:
- 源点:水流出发的点
- 汇点:水流的汇聚点
最大流有很多的求法,今天曰一个代码比较简单的——FF算法,思路可以说是简单至极呀!就是不停地用DFS去找增广路(就是任意一条能从源点到汇点的路),记录每条边上剩余的流量,把答ans加上,直到找不到了为止,这是的ans就是答案。
立马有大佬发言:我有反例,Look at the 图
假设第一次找到了这条增广路:
然后图就变成了这样:
结果找不到增广路了,得到答案100.BUT肉眼一观察就知道是200呀!错了!嘿嘿嘿~~
杨子曰:杨子曰的永远是真理,我们在找到增广路后要对路径上的边建一条反向边,在反方向的那条边的流量上增加流过的值,比如找到了刚才那条增广路,我们要把图变成这样:
然后再找,找到1→3→2→4,对于3→2这条边又想左流过100,所以反方向2→3这条边的流量要加上100,图就变成这样:
其实就是:
这回是真的找不到任何增广路了,得到答案200,完事
立马就有人提出了问题,为什么可以建一条反向边?因为反向边可以反悔你犯下的错误,当你在找到一条路径后,它不一定很优,这是你可以找另一条反方向的边来抵消它,你也可以这样理解:你向右流了100的水流,就相当于向左流了-100的水流,假设原水管的流量是500,你就可以向左流600了,懂否?
FF算法通过不断的DFS灌水找增广路,直到这不到了为止实现的,写代码时要注意:
由于是灌水,所以要有vis数组,注意每次找增广路前要清空
用邻接表存边的时候要把每组相反的边用编号(0,1)一组,(2,3)一组……,Why?因为之后你可以通过异或1(^1)来找到它的反方向的边
还有一点,你可以在DFS回溯的时候顺便把条边和反向边更新掉
顺便提一下,所有反向边开始时剩余流量设为0
代码走起:
int dfs(int x,int flow){//flow表示流到x,还剩下的水流量
if (x==n) return flow;
vis[x]=1;
for (int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to,tmp;
if (!vis[v] && edge[i].c && (tmp=dfs(v,min(flow,edge[i].c)))){
edge[i].c-=tmp;
edge[i^1].c+=tmp;
return tmp;
}
}
return 0;
}
对于这个算法的复杂度,可以说是很玄学呀!FF算法的复杂度就是传说中的玄学复杂度——O(n*m^2),一算,啊呀!TLE了;一交,啊呀!AC了,在实际生活中它的复杂度远远不及这个值,So,放心大胆地去交你TLE的代码吧!(真的TLE了别来怪我)
OK,完事
c++完整模板(HihoCoder - 1369)
#include<bits/stdc++.h>
using namespace std;
const int maxn=505,maxm=20005,inf=200000000;
struct Edge{
int next,to,c;
}edge[maxm*2];
int head[maxn],vis[maxn];
int n,m,nedge=0;
void addedge(int x,int y,int z){
edge[nedge]=(Edge){head[x],y,z}; head[x]=nedge++;
edge[nedge]=(Edge){head[y],x,0}; head[y]=nedge++;
}
int dfs(int x,int flow){
if (x==n) return flow;
vis[x]=1;
for (int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to,tmp;
if (!vis[v] && edge[i].c && (tmp=dfs(v,min(flow,edge[i].c)))){
edge[i].c-=tmp;
edge[i^1].c+=tmp;
return tmp;
}
}
return 0;
}
int main(){
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
int tmp,ans=0;
while(tmp=dfs(1,inf)){
memset(vis,0,sizeof(vis));
ans+=tmp;
}
cout<<ans;
return 0;
}
于 XJZX 507机房
未经作者允许,严禁转载:https://blog.csdn.net/HenryYang2018/article/details/81027302