• Dinic算法


    ( ext{Dinic})相对( ext{EK})的高效之处在于运用了分层图(即由满足( ext{dep[v]=dep[u]+1})的边((u,v))构成的子图,为有向无环图),当考虑流向的点在分层图中深度比当前点大1 时才向那个点走,去尝试找增广路。不用担心联通性可能在这个分层图中被破坏,它在之后的分层中还是会考虑到的;不需要在意这条增广路是否为最优,只要走就是了,反正还是有反悔的机会的。
    时间复杂度:(O(n^2m)),实际运用远达不到这个上界,一般能处理(10^4)~(10^5)规模的网络。

    怎么实现呢?

    1. 在残量网络上使用BFS构造分层图
    2. 在分层图上使用DFS尝试寻找增广路,并且实时更新每条边的容量
    3. 重复执行1.2直到分层图中s不能到达t(没有增广路)为止

    在优化( ext{BFS})次数之后,我们还可以进行优化——当前弧优化

    对于在不同的分层图进行( ext{DFS})的过程中,不重复走之前走过的满流的边,因为再走下去终究会卡住,是白做工。可以用一个数组( ext{cur})记录一下当前点更新到哪条边了,具体看代码。

    #include <cstdio>
    #include <cstring>
    const int maxn=10000+10;
    const int maxm=100000+10;
    const int INF=0x3f3f3f3f; 
    int cur[maxn],head[maxn],nxt[maxm<<1],to[maxm<<1],val[maxm<<1];//因为还要存反向边,所以要开两倍 
    int dep[maxn],inq[maxn];
    int n,m,s,t;
    int tot=1;
    struct Queue
    {
    	int a[maxn];
    	int l,r;
    	Queue() {l=1,r=0;}
    	void push(int x) {a[++r]=x;}
    	void pop() {l++;}
    	int front() {return a[l];}
    	bool empty() {return l>r;}
    }q;
    
    inline int min(int x,int y) {return x<y?x:y;}
    void add(int u,int v,int w)
    {
        nxt[++tot]=head[u];
        head[u]=tot;
        to[tot]=v;
        val[tot]=w;
    }
    bool bfs()
    {
        memset(dep, 0x3f, sizeof(dep));
        dep[s]=0;
        q=Queue();
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for (int i=head[u];i;i=nxt[i])
            {
                int v=to[i];//通向的点 
                if (val[i]&&dep[v]>dep[u]+1)//如果容量不为0且在u点之前还没有被搜到
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            } 
        }
        return dep[t]<INF;//只要汇点被搜到了,就还有增广路 
    }
    int dfs(int u,int minf)//当前位置和目前搜到的最小剩余容量
    {
        if (u==t)//到达汇点
            return minf;//返回值不为0即说明可以增广
    
        int used=0;//该点已经使用了的流量
        for (int &i=cur[u];i;i=nxt[i])//这里取址是顺便更新cur
        {
            int v=to[i];
            if (val[i]&&dep[v]==dep[u]+1)
            {
            	int flow=dfs(v, min(minf-used, val[i]));//能流到t的流量 
                if (flow)
                {
                    used+=flow;
                    val[i]-=flow;
                    val[i^1]+=flow;
                    if (used==minf)//该点已达最大流量,不用继续找了 
                        break; 
                }
            }
         }
         return used;//返回该点已使用流量 
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&s,&t);
        for (int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u, v, w),add(v, u, 0);
        }
        int flag=0,maxflow=0; 
        while(bfs())
        {
        	for (int i=1;i<=n;i++)//新的分层图要重新开始
        		cur[i]=head[i];
        	maxflow+=dfs(s, INF);
        }
        printf("%d
    ",maxflow);
        return 0;
    }
    
  • 相关阅读:
    Java中的包装类
    Java中的StringBuffer和StringBuilder
    Java中的String类
    分类模型评价指标:混淆矩阵;ROC曲线;AUC面积
    matlab神经网络多分类(模式识别神经网络nprtool)
    matlabKNN多分类
    matlabSVM多分类:fitcecoc函数
    随机森林和GBDT
    matlab数据预处理
    CDN加速地址URL拿不到,显示“无法访问此网站”
  • 原文地址:https://www.cnblogs.com/hkr04/p/Dinic.html
Copyright © 2020-2023  润新知