网络流之最大流——杨子曰算法

网络流之最大流(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

猜你喜欢

转载自blog.csdn.net/henryyang2018/article/details/81027302