最大流模板(EK,Dinic
一、EK
EK算法:用bfs找增广路直到找不到为止。找到则更新最大流和残余网络,找不到则结束。
残余网络:对于一条走过的边,其正向边权值减少相应值,反向边权值增加相应值(用于反悔)。
增广路:从所求起点到终点之间还可以增大流量的路径。
复杂度O(n*m^2)
#include<bits/stdc++.h> using namespace std; #define ll long long int n,m,s,t; const int maxn=220; ll G[maxn][maxn],flow[maxn],pre[maxn];//flow:源点到当前点的流量,pre增广路的上一条边 ll bfs(int s,int t){//找增广路 queue<int>qu; while(!qu.empty())qu.pop(); memset(pre,-1,sizeof pre);//记录前驱 pre[s]=0; flow[s]=0x3f3f3f3f; qu.push(s); while(!qu.empty()){ int p=qu.front();qu.pop(); if(p==t)break; for(int i=1;i<=n;i++){ if(i!=s&&G[p][i]>0&&pre[i]==-1){ pre[i]=p; flow[i]=min(flow[p],G[p][i]);//选承载量最小的 qu.push(i); } } } if(pre[t]==-1)return -1; return flow[t]; } ll EK(int s,int t){ ll ans=0,tot=0; while(1){ ans=bfs(s,t); if(ans==-1)break; int p=t; while(p!=s){//回溯整条增广路,进行更新 G[pre[p]][p]-=ans; G[p][pre[p]]+=ans;//反向边 p=pre[p]; } tot+=ans; } return tot; } int main() { int i,j; cin>>n>>m>>s>>t; memset(G,0,sizeof G); memset(flow,0,sizeof flow); for(i=0;i<m;i++){ int a,b;ll c;cin>>a>>b>>c; G[a][b]+=c;//累计容量 防止重边 } cout<<EK(s,t); return 0; }
二、Dinic
有时候EK会超时 因为可能会出现增广路经过的其中一条边值为1,而其他边值很大的情况,则需要一直增广。
而Dinic利用分层可以一次dfs实现多次增广,从而优化EK算法。
Dinic算法:先利用bfs进行分层(只能往层数+1的地方走),再利用dfs实现进行增广(一次dfs实现多次增广)。该步骤一直循环直到不可分层为止。
复杂度O(m*n^2)
#include <bits/stdc++.h> using namespace std; const int maxn = 50005; const int maxm=500010;//边 const int inf =0x3f3f3f3f; int head[maxn],dis[maxn]; struct Edge { int to,next,f; }edge[maxm]; //链式前向星 int s,t,cnt; void add(int u,int v,int f) { edge[cnt].to=v; edge[cnt].f=f; edge[cnt].next=head[u]; head[u]=cnt++; //正向建边//相邻边则为反向边,cnt从0开始(1不行) edge[cnt].to=u; edge[cnt].f=0; edge[cnt].next=head[v]; head[v]=cnt++; //反向建边 } bool bfs() { memset(dis,-1,sizeof(dis)); queue <int> que; dis[s]=0; que.push(s); while(!que.empty()) { int u=que.front(); que.pop(); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; int f=edge[i].f; if(dis[v]==-1&&f>0)//有流量且未访问过 { dis[v]=dis[u]+1;//分层 if(v==t) return true; que.push(v); } } } return false; } int dfs(int x,int maxf) //maxf表多少流量流到当前节点 { if(x==t||maxf==0) return maxf; int flow=0; for(int i=head[x];i!=-1;i=edge[i].next) { int v=edge[i].to; int f=edge[i].f; if(dis[v]==dis[x]+1&&f>0) { f=dfs(v,min(f,maxf-flow));//当前边的容量和该点剩余量取min edge[i].f-=f; edge[i^1].f+=f;//相邻边则为反向边,通过异或可以直接找到反向边 flow+=f; if(flow==maxf) return flow; } } return flow; } int main() { int T,n,m,k; cin>>n>>m>>s>>t; cnt=0; memset(head,-1,sizeof(head)); for(int i=0;i<m;i++) { int u,v,f; scanf("%d%d%d",&u,&v,&f); add(u,v,f); //加边 } int ans=0; while(bfs()) ans+=dfs(s,inf); cout<<ans<<endl; }
经过bfs分层后有123三层,从s出发,只会往第二层的三个点依次进行dfs。dfs手动模拟即可理解,每次走过时记得更新残余网络。