zoukankan      html  css  js  c++  java
  • 浅谈网络流Dinic算法

    浅谈网络流Dinic算法

    本篇随笔简单讲解一下网络流中的Dinic算法。


    一、前置知识

    浅谈网络最大流

    一些基本定义

    1、弧:网络上的有向边被称作弧。弧分为前向弧和后向弧。前向弧就是题目中给出的有向边,后向弧就是我们所建立的反边。

    这样地、弧就有了容量、流量、零流弧、饱和弧这些建立在网络流边上的定义。

    2、链:网络的一条顶点序列被称作链。注意,只要是相连的顶点序列就可以,不一定要求弧的方向相同。所以一条链中既有前向弧,又有后向弧。


    二、Dinic算法的原理

    其大致步骤大致为:

    在网络上通过(BFS)为其划分层次。层次在我理解中,是和深度差不多的概念。但是深度只在树上划分,而层次就是图上的。我们可以理性地将其理解为从源点到此处的最短路。

    但是这个层次划分是有条件的,我们要求所有的点必须可达,也就是从源点到它的链上不能有零流弧。

    然后再用DFS寻找。

    这个算法的时间复杂度最坏是(O(n^2m))

    其时空复杂度的优化主要是因为这个“分层”。分层操作预处理出所有点到源点的距离,所以我们增广的时候,只向层数高的地方增广,可以保证不走回头路,不绕圈,这样就排除了很多冗余搜索,大大提升时空复杂度。


    三、Dinic算法的优化

    Dinic算法的优化方式有两种,第一种是多路增广,第二种是当前弧优化。

    理论复杂度是(O)(可过)——LLQ

    多路增广

    我们可以使用多路增广节省很多花在重复路线上的时间:在某点DFS找到一条增广路后,如果还剩下多余的流量未用,继续在该点DFS尝试找到更多增广路。

    当前弧优化

    在Dinic算法中,一条边增广一次后就不会再次增广了,所以下次增广时不需要再考虑这条边。我们把head数组复制一份,但不断更新增广的起点。


    四、Dinic算法的代码实现

    还是以模板题为例。

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define int long long
    using namespace std;
    const int maxn=210;
    const int maxm=5010;
    const int INF=2147483646;
    int n,m,s,t,ans;
    int tot=1,head[maxn],nxt[maxm<<1],to[maxm<<1],val[maxm<<1];
    int lv[maxn],cur[maxn];
    queue<int> q;
    void add(int x,int y,int z)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        val[tot]=z;
        head[x]=tot;
    }
    bool bfs()
    {
        memset(lv,0,sizeof(lv));
        lv[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(!val[i]||lv[y])
                    continue;
                lv[y]=lv[x]+1;
                q.push(y);
            }
        }
        return lv[t];
    }
    int dfs(int x,int flow)
    {
        if(!flow||x==t)
            return flow;
        int ret=0;
        for(int i=cur[x];i;i=nxt[i])
        {
            cur[x]=i;
            int y=to[i];
            if(val[i]>0 && lv[y]==lv[x]+1)
            {
                int tmp=dfs(y,min(val[i],flow));
                flow-=tmp;
                ret+=tmp;
                val[i]-=tmp;
                val[i^1]+=tmp;
            }
        }
        return ret;
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            add(x,y,z);
            add(y,x,0);
        }
        while(bfs())
        {
            memcpy(cur,head,sizeof(head));
            ans+=dfs(s,INF);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    使用C# impersonation进行windows帐号的校验
    今天,如何将你现有的网站移动互联网化【译】
    吐槽一下拖延的生活状态
    Web前端开发工程师编程能力飞升之路【转】
    公司框架集成log4net时遇到的问题
    我所认知的电子商务解决方案
    Web开发攻城狮,欲善其事先利其器
    调用Web Service服务出现响应消息的内容类型text/html与绑定text/xml的内容类型不匹配
    服务器 '***' 上的 MSDTC 不可用
    Firefox中无法使用window.close()关闭窗口
  • 原文地址:https://www.cnblogs.com/fusiwei/p/14055942.html
Copyright © 2011-2022 走看看