zoukankan      html  css  js  c++  java
  • Dinic算法----最大流常用算法之一

    ——没有什么是一个BFS或一个DFS解决不了的;如果有,那就两个一起。

    最大流的$EK$算法虽然简单,但时间复杂度是$O(nm^2)$,在竞赛中不太常用。

    竞赛中常用的$Dinic$算法和$SAP$,其实也不太难。

    那么,$Dinic$算法到底是什么呢?


    多路增广

    $Dinic$算法最核心的内容就是多路增广

    沿着$EK$算法的过程:

    我们有一个图,如图一。

    按照套路,我们先$BFS$,找$S-T$最短路。所有的距离标号都画在了图二上($EK$算法可能用不到,但$Dinic$用得到)。

    假设我们选的是$S-3-T$这条路,增广。。。(如图三,绿色)

    然后我们再来一遍$BFS$。。。 等等!

    细心的你可能也发现了,$S-1-T$也是一条$S-T$最短路。

    那就增广吧!(如图四)

    您可以检查一下,这时候没有长度为$2$的最短路了。

    但EK算法不会这样。它会再笨拙地$BFS$一遍,这就浪费了不少时间。

    所以说,多路增广是很重要的。

    我们换一种思路,如果网络流在一个$DAG$上,还不用考虑回退边,你会怎么做?

    这很简单,$dfs$就能解决。

    至于回退边。。。再来一次$BFS-DFS$就好了啊。

    还有一个优化:当前弧优化:

    对于每个点,我可能在一次$BFS$之后$DFS$多次。那么它出发的边所到的点里, 有些点出发已经满流。

    这样, 我就可以每个点记录一个当前弧, 表示这次$DFS$它最后$DFS$到哪条弧,下次$DFS$它的时候就从这条弧开始。

    这样,我就可以保证每条边在一次$DFS$中满流后不会再遍历。

    这样的复杂度。。。理论上最坏是$O(n^2m)$,但这上界很松。

    附代码!

     1 int n;
     2 
     3 struct Dinic{
     4     struct Edge{
     5         int from, to;
     6         LL cap, flow;
     7         Edge(int f = -1, int t = -1, LL c = 0)
     8         :from(f), to(t), cap(c), flow(0)
     9         {}
    10     }edges[MAXM];
    11     int next[MAXM], cnt;
    12     int pre[MAXN], dis[MAXN];
    13     int cur[MAXN];                                    //当前弧
    14     Dinic()
    15     {
    16         memset(pre, -1, sizeof(pre));
    17         cnt = 0;
    18     }
    19     void addedge(int f, int t, LL c)
    20     {
    21         edges[cnt] = Edge(f, t, c);
    22         next[cnt] = pre[f];
    23         pre[f] = cnt++;
    24         edges[cnt] = Edge(t, f, 0);
    25         next[cnt] = pre[t];
    26         pre[t] = cnt++;
    27     } 
    28     queue<int> Q;
    29     bool BFS(int s, int t)
    30     {
    31         while(!Q.empty()) Q.pop();
    32         memset(dis, -1, sizeof(dis));
    33         dis[s] = 0;
    34         Q.push(s);
    35         while(!Q.empty())
    36         {
    37             int u = Q.front(); Q.pop();
    38             for(int i = pre[u]; i >= 0; i = next[i]) if(edges[i].cap > edges[i].flow)
    39             {
    40                 int v = edges[i].to;
    41                 if(dis[v] >= 0) continue;
    42                 dis[v] = dis[u] + 1;
    43                 if(v == t) return true;
    44                 Q.push(v);
    45             }
    46         }
    47         return false;
    48     }
    49     LL DFS(int now, int t, LL maxflow)            //当前在now,汇点t
    50     {                                             //最大可以提供maxflow的流量 
    51         if(now == t) return maxflow;
    52         int ret = 0;
    53         for(int i = cur[now] != -1 ? cur[now] : pre[now]; i >= 0; i = next[i]) if(edges[i].cap > edges[i].flow) 
    54         {
    55             int v = edges[i].to;
    56             if(dis[v] != dis[now] + 1) continue;
    57             int l = DFS(v, t, min(edges[i].cap - edges[i].flow, maxflow - ret));
    58             ret += l;
    59             edges[i].flow += l;
    60             edges[i^1].flow -= l;
    61             cur[now] = i;
    62             if(ret == maxflow) return ret;
    63         }
    64         cur[now] = -2;
    65         return ret;
    66     }
    67     LL solve(int s, int t)
    68     {
    69         int res = 0;
    70         while(BFS(s,t)) 
    71         {
    72             memset(cur, -1, n * sizeof(int));
    73             res += DFS(s, t, inf);
    74         }
    75         return res;
    76     }
    77 };
    Dinic

    代码可能有错,烦请指出。谢谢。

    另外,如果有人知道些好用的画图软件麻烦推荐一下。用windows自带画图太累了。

  • 相关阅读:
    单词统计-续
    “帮你APP”团队冲刺10
    软件工程周总结15
    梦断代码阅读笔记03
    个人课程总结
    梦断代码阅读笔记02
    梦断代码阅读笔记01
    软件工程周总结14
    计算最长英语单词链
    软件工程周总结13
  • 原文地址:https://www.cnblogs.com/y-clever/p/6308820.html
Copyright © 2011-2022 走看看