上回介绍了EK算法,这次来学一个效率更高的算法。我们在做EK算法的时候,每次bfs最多遍历了整个残量网络,但是只找到了一条增广路。所以有可以优化的地方。Dinic算法基于分层图,我们先bfs找到一个合法的有向无环图(图中所有边都有残流,图严格按照拓扑序),然后在这张分层图上通过深度优先搜索找到若干条合法不冲突的增广路,累加到maxflow里。并且可以在dfs里添加剪枝:如果当前点以后找不到残流路径,把它直接从分层图中删掉。
Dinic算法的时间复杂度为O(n^2*m)。实际运用远远小于这个上界,可以说是比较容易实现的效率最高的网络流算法之一,一般可以处理10^4~10^5的网络。特别的,Dinic算法求解二分图最大匹配的时间复杂度为O(m*sqrt(n))。
//Dinic求二分图最大匹配 #include<bits/stdc++.h> using namespace std; const int inf=1<<29,N=20100,M=2e6+20; int n,m,e,tot=1,s,t,maxflow,flow,d[N],Next[M],ver[M],lin[N],edge[M]; void add(int x,int y,int c){ ver[++tot]=y;Next[tot]=lin[x];edge[tot]=c;lin[x]=tot; ver[++tot]=x;Next[tot]=lin[y];edge[tot]=0;lin[y]=tot; } bool bfs(){ memset(d,0,sizeof(d)); queue<int>q; q.push(s);d[s]=1; while(q.size()){ int x=q.front();q.pop(); for(int i=lin[x];i;i=Next[i]){ int y=ver[i]; if(edge[i]&&!d[y]){ d[y]=d[x]+1; q.push(y); if(y==t) return 1; } } } return 0; } int dinic(int x,int flow){ if(x==t) return flow; int rest=flow; for(int i=lin[x];i&&rest;i=Next[i]){ int y=ver[i]; if(d[y]==d[x]+1&&edge[i]){ int k=dinic(y,min(rest,edge[i])); if(!k) d[y]=0; rest-=k; edge[i]-=k; edge[i^1]+=k; } } return flow-rest; } int main(){ scanf("%d%d%d",&n,&m,&e); s=0,t=n+m+1; for(int i=1;i<=e;++i){ int x,y;scanf("%d%d",&x,&y); if(y>m) continue; add(x,y+n,1); } for(int i=1;i<=n;++i) add(s,i,1); for(int i=1;i<=m;++i) add(i+n,t,1); while(bfs()) while(flow=dinic(s,inf)) maxflow+=flow; printf("%d\n",maxflow); return 0; }