Dinic算法思路
- 根据残量网络计算层次图
- 在层次图中使用DFS进行增广直到不存在增广路
- 重复以上步骤直到无法增广
首先对每条弧存一条反向弧,初始流量为0,当正向弧剩余流量减少时,反向弧剩余流量随之增加,这样就为每条弧提供了一个反悔的机会,可以让一个流沿反向弧退回而去寻找更优的路线。对于一个网络流图,用bfs将图分层,只保留每个点到下一个层次的弧,目的是减少寻找增广路的代价。对于每一次可行的增广操作,用dfs的方法寻找一条由源点到汇点的路径并获得这条路径的流量c。根据这条路径修改整个图,将所经之处正向边流量减少c,反向边流量增加c。如此反复直到bfs找不到可行的增广路线。
当前弧优化
对于一个节点x,当它在dfs中走到了第i条弧时,前i-1条弧到汇点的流一定已经被流满而没有可行的路线了。那么当下一次再访问x节点的时候,前i-1条弧就可以被删掉而没有任何意义了。所以我们可以在每次枚举节点x所连的弧时,改变枚举的起点,这样就可以删除起点以前的所有弧以达到优化的效果。
【模板】网络最大流 洛谷P3376
#include<bits/stdc++.h>
#define INF 10000000
using namespace std;
int head[10005],nex[200005],tail[200005],cap[200005],tp=-1,dep[10005];
int fir[10005];
int n,m,s,t,a,b,v;
int dfs(int x,int now)
{
if(!now || x==t) return now;
int c=0;
for(int &i=fir[x];i!=-1;i=nex[i])
{
if(cap[i] && dep[tail[i]]==dep[x]+1)
{
int f=dfs(tail[i],min(now,cap[i]));
c+=f;
now-=f;
cap[i]-=f;
cap[i^1]+=f;
if(!now) break;
}
}
return c;
}
inline bool bfs()
{
memset(dep,0,sizeof(dep));
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i!=-1;i=nex[i])
{
if(cap[i]&&!dep[tail[i]])
{
dep[tail[i]]=dep[x]+1;
q.push(tail[i]);
}
}
}
return dep[t];
}
void add(int x,int y,int v)
{
nex[++tp]=head[x];
head[x]=tp;
tail[tp]=y;
cap[tp]=v;
}
inline int Dinic()
{
int c=0;
while(bfs())
{
for(int i=1;i<=n;++i) fir[i]=head[i];
c+=dfs(s,INF);
}
return c;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&v);
add(a,b,v);
add(b,a,0);
}
printf("%d",Dinic());
return 0;
}