——没有什么是一个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 };
代码可能有错,烦请指出。谢谢。
另外,如果有人知道些好用的画图软件麻烦推荐一下。用windows自带画图太累了。