zoukankan      html  css  js  c++  java
  • 网络流 最大流dinic算法解释

    网络流 最大流dinic算法解释

    关于最大流的求解

    1. 找到一条从源点到汇点的路径,使得路径上任意一条边的残量>0(注意是小于而不是小于等于,这意味着这条边还可以分配流量),这条路径便称为增广路
    2. 找到这条路径上最小的F[u][v](我们设F[u][v]表示u->v这条边上的残量即剩余流量),下面记为flow
    3. 将这条路径上的每一条有向边u->v的残量减去flow,同时对于起反向边v->u的残量加上flow
    4. 重复上述过程,直到找不出增广路,此时我们就找到了最大流

    这个算法是基于增广路定理(Augmenting Path Theorem)

    引入dinic算法

    dinic算法实质上加上了分层图的思想,即每次更新残量网络上最短路的增广路。
    参考一个比较优秀的代码
    一些变量的定义

    int s,t;//源点和汇点
    int cnt;//边的数量,从0开始编号。
    int Head[maxN];//每一个点最后一条边的编号
    int Next[maxM];//指向对应点的前一条边
    int V[maxM];//每一条边指向的点
    int W[maxM];//每一条边的残量
    int Depth[maxN];//分层图中标记深度
    

    Dinic主过程:

    int Dinic()
    {
        int Ans=0;//记录最大流量
        while (bfs())
        {
            while (int d=dfs(s,inf))
                Ans+=d;
        }
        return Ans;
    }
    

    bfs分层图过程

    bool bfs()
    {
        queue<int> Q;//定义一个bfs寻找分层图时的队列
        while (!Q.empty())
            Q.pop();
        memset(Depth,0,sizeof(Depth));
        Depth[s]=1;//源点深度为1
        Q.push(s);
        do
        {
            int u=Q.front();
            Q.pop();
            for (int i=Head[u];i!=-1;i=Next[i])
                if ((W[i]>0)&&(Depth[V[i]]==0))//若该残量不为0,且V[i]还未分配深度,则给其分配深度并放入队列
                {
                    Depth[V[i]]=Depth[u]+1;
                    Q.push(V[i]);
                }
        }
        while (!Q.empty());
        if (Depth[t]==0)//当汇点的深度不存在时,说明不存在分层图,同时也说明不存在增广路
            return 0;
        return 1;
    }
    

    dfs寻找增广路过程

    int dfs(int u,int dist)//u是当前节点,dist是当前流量
    {
        if (u==t)//当已经到达汇点,直接返回
            return dist;
        for (int i=Head[u];i!=-1;i=Next[i])
        {
            if ((Depth[V[i]]==Depth[u]+1)&&(W[i]!=0))//注意这里要满足分层图和残量不为0两个条件
            {
                int di=dfs(V[i],min(dist,W[i]));//向下增广
                if (di>0)//若增广成功
                {
                    W[i]-=di;//正向边减
                    W[i^1]+=di;反向边加
                    return di;//向上传递
                }
            }
        }
        return 0;//否则说明没有增广路,返回0
    }
    

    把上面的内容都封装到类中:
    在此处加上了当前弧优化,即每次不从1开始搜,而是从cur[i]开始。

    class Graph
    {
    private:
        int cnt;
        int Head[maxN];
        int Next[maxM];
        int W[maxM];
        int V[maxM];
        int Depth[maxN];
        int cur[maxN];//cur就是记录当前点u循环到了哪一条边
    public:
        int s,t;
        void init()
            {
                cnt=-1;
                memset(Head,-1,sizeof(Head));
                memset(Next,-1,sizeof(Next));
            }
        void _Add(int u,int v,int w)
            {
                cnt++;
                Next[cnt]=Head[u];
                Head[u]=cnt;
                V[cnt]=v;
                W[cnt]=w;
            }
        void Add_Edge(int u,int v,int w)
            {
                _Add(u,v,w);
                _Add(v,u,0);
            }
        int dfs(int u,int flow)
            {
                if (u==t)
                    return flow;
                for (int& i=cur[u];i!=-1;i=Next[i])
                {
                    if ((Depth[V[i]]==Depth[u]+1)&&(W[i]!=0))
                    {
                        int di=dfs(V[i],min(flow,W[i]));
                        if (di>0)
                        {
                            W[i]-=di;
                            W[i^1]+=di;
                            return di;
                        }
                    }
                }
                return 0;
            }
        int bfs()
            {
                queue<int> Q;
                while (!Q.empty())
                    Q.pop();
                memset(Depth,0,sizeof(Depth));
                Depth[s]=1;
                Q.push(s);
                do
                {
                    int u=Q.front();
                    Q.pop();
                    for (int i=Head[u];i!=-1;i=Next[i])
                        if ((Depth[V[i]]==0)&&(W[i]>0))
                        {
                            Depth[V[i]]=Depth[u]+1;
                            Q.push(V[i]);
                        }
                }
                while (!Q.empty());
                if (Depth[t]>0)
                    return 1;
                return 0;
            }
        int Dinic()
            {
                int Ans=0;
                while (bfs())
                {
                    for (int i=1;i<=n;i++) cur[i]=Head[i];
                    while (int d=dfs(s,inf))
                    {
                        Ans+=d;
                    }
                }
                return Ans;
            }
    };
    
  • 相关阅读:
    HDU 4644 BWT (KMP)
    常数的值类型问题
    HDU 1395 2^x mod n = 1 (欧拉函数)
    HDU 5384 Danganronpa(AC自动机)
    9.自己实现linux中的tree
    8.底层文件库
    7.标准文件库
    7.gcc的使用
    5.文件I/O
    4.vim操作
  • 原文地址:https://www.cnblogs.com/patricksu/p/7929812.html
Copyright © 2011-2022 走看看