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自带画图太累了。

posted @ 2017-01-19 21:17  _rqy  阅读(9242)  评论(4编辑  收藏  举报