昨天写了网络流的EK算法(写得我自己都不太懂),今天再来总结一下另一个网络最大流的算法Dinic。
先来介绍一个概念:残量网络。
一个网络中所有剩余容量大于0的边和与这些边相连的节点组成这个网络的残量网络。
我们回忆一下EK算法的过程:
1.在网络中寻找一条剩余容量大于0的边,把它加入到增广路中。
2.依据找到的增广路,更新边的剩余容量和最大流。
所以,我们每次对增广路的搜索,都可能需要对整个残量网络进行遍历,而只找到一条增广路,还可以进一步优化。
在这里,我们提出了节点的层次d[x],它表示从s到x最少需要经过的边数。在残量网络中,满足d[y] = d[x] + 1的所有边(x , y)构成的子图被称为分层图。显然,它是一张有向无环图。
而传说中的Dinic算法就是通过以下两个步骤来求最大流:
1.在残量网络中不断通过BFS构造分层图,直到s不能到达t。
2.在分层图上用DFS求增广路,在回溯时更新剩余容量。
好啦,大体的介绍完了,具体是怎么回事呢??(其实我也不太懂)
在BFS时,我们每找到一个节点x,它到s的最少边数d[x]一定已经求得,那么所有以x为起点的边(x , y)(剩余容量大于0)都可以成为分层图中的子图,于是我们把所有与x相连的节点y的d[y](它还没有被赋值)都更新为d[y] = d[x] + 1,因为只需要多走一条边就可以到y了。这样,在BFS结束时,分层图就已经构建出来了。
然后我们在这张分层图上DFS,直到找到汇点t时就回溯,同时更新DFS时经过的每一条边的剩余容量就可以了,最后每次都更新一下最大流maxflow(然后你就AC啦)。时间复杂度比EK快一点,为O(mn^2)其实都是O(能过)
QAQ还是那道题:洛谷P3376模板题
然后我的好看的代码:
#include<bits/stdc++.h> using namespace std; const int inf = 1 << 29,N = 10100,M = 100100; int n,m,s,t,maxflow,tot = 1; int ver[M*2],edge[M*2],Next[M*2],head[M*2]; void add(int x,int y,int z){ ver[++tot] = y;edge[tot] = z;Next[tot] = head[x];head[x] = tot; ver[++tot] = x;edge[tot] = 0;Next[tot] = head[y];head[y] = tot;//依然要反向建边。 } queue<int>q; int d[N];//d[x]记录s到x要经过的最少边数。 bool bfs(){ memset(d,0,sizeof(d)); while(q.size()) q.pop(); q.push(s);d[s] = 1; while(q.size()){ int x = q.front();q.pop(); for(int i = head[x];i;i = Next[i]){ if(edge[i]){//在残量网络中寻找。 int y = ver[i]; if(d[y]) continue;//如果d[y]有值,说明在x之前已经搜过了,不再搜。 d[y] = d[x] + 1;//没搜过就更新d[y]。 q.push(y); if(y == t) return 1;//如果到达t,说明分层图建完了。 } } } return 0;//未到达说明以求得网络最大流。 } int dinic(int x,int flow){//x为当前节点,flow为当前流量。 if(x == t) return flow;//搜到t,结束并回溯。 int rest = flow,k; for(int i = head[x];i;i = Next[i]){ if(edge[i]){//依旧在残量网络中搜索。 int y = ver[i]; if(d[y] == d[x] + 1){//如果是分层图中的点,说明可以有增广路。 k = dinic(y,min(rest,edge[i]));//接着搜索y,当前流为rest和edge[i]中更小的。 if(!k) d[y] = 0;//如果k==0,说明无法到达 t了,剪枝,从分层图中删去。 edge[i] -= k;//回溯时更新。 edge[i^1] += k; rest -= k;//剩余流减少,因为要返回flow - rest。 } } } return flow - rest; } int main(){ cin>>n>>m; cin>>s>>t; for(int i = 1;i <= m; i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); } int flow = 0; while(bfs()) while((flow = dinic(s,inf))) maxflow += flow;//更新最大流。 printf("%d",maxflow); return 0; }
网络瘤太毒了,我真得只是学了一点点皮毛。。。