一般的网络流算法,每进行一次增广,都要做 一遍BFS,十分浪费。能否少做几次BFS? 这就是Dinic算法要解决的问题。
Edmonds-Karp的提高余地:需要多次从s到t调 用BFS,可以设法减少调用次数。亦即:使用一种代价较小的高效增广方法。 考虑:在一次增广的过程中,寻找多条增广路 径。然后进行 DFS。
先利用 BFS对残余网络分层,每一个节点的层数就是从源点到它最少要经历的边数。利用BFS对残余网络进行分层,然后再用DFS向前一层向后一层不断查找增广路(即要求DFS的每一步都必须走到下一层的节点处)。因此,前面在分层时,只要进行到汇点的层次数被算出 即可停止,因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点的。DFS过程中,要是碰到了汇点,则说明找到了一条增广路 径。此时要增加总流量的值,消减路径上各边的容量 ,并添加反向边,即所谓的进行增广。
DFS找到一条增广路径后,并不立即结束,而是回溯后继 续DFS寻找下一个增广路径。 回溯到哪个节点呢? 回溯到的节点u满足以下条件: 1) DFS搜索树的树边(u,v)上的容量已经变成0。即刚刚 找到的增广路径上所增加的流量,等于(u,v)本次增广 前的容量。(DFS的过程中,是从u走到更下层的v的) 2)u是满足条件 1)的最上层的节点 如果回溯到源点而且无法继续往下走了,DFS结束。 因此,一次DFS过程中,可以找到多条增广路径。 DFS结束后,对残余网络再次进行分层,然后再进行DFS 当残余网络的分层操作无法算出汇点的层次(即BFS到达 不了汇点)时,算法结束,最大流求出。 一般用栈实现DFS,这样就能从栈中提取出增广路径。
将原图备份,原图上的边的容量减去做完最大 流的残余网络上的边的剩余容量,就是边的 流量。
#include<bits/stdc++.h>
#define ll long long
#define INF 999999999
using namespace std;
const ll mod=1e9+7;
const ll maxn=1e6+7;
int g[300][300];
bool vis[300];
int layer[300];
int n,m;
bool countlayer()
{
int la=0;
deque<int>q;
memset(layer,0xff,sizeof(layer));
q.push_back(1);
layer[1]=0;
while(!q.empty())
{
int v=q.front();
q.pop_front();
for(int j=1; j<=m; j++)
{
if(g[v][j]>0&&layer[j]==-1)
{
layer[j]=layer[v]+1;
if(j==m)
{
return true;
}
else
{
q.push_back(j);
}
}
}
}
return false;
}
int dinic()
{
int i;
int s;
int nm=0;
deque<int>q;
while(countlayer())
{
q.push_back(1);
memset(vis,0,sizeof(vis));
vis[1]=1;
while(!q.empty())
{
int nd=q.back();
if(nd==m)
{
int nmc=INF;
int nmc_vs;
for(i=1; i<q.size(); i++)
{
int vs=q[i-1];
int ve=q[i];
if(g[vs][ve]>0)
{
if(nmc>g[vs][ve])
{
nmc=g[vs][ve];
nmc_vs=vs;
}
}
}
nm+=nmc;
for(i=1; i<q.size(); i++)
{
int vs=q[i-1];
int ve=q[i];
g[vs][ve]-=nmc;
g[ve][vs]+=nmc;
}
while(!q.empty()&&q.back()!=nmc_vs)
{
vis[q.back()]=0;
q.pop_back();
}
}else
{
for(i=1;i<=m;i++)
{
if(g[nd][i]>0&&layer[i]==layer[nd]+1&&!vis[i])
{
vis[i]=1;
q.push_back(i);
break;
}
}
if(i>m)
{
q.pop_back();
}
}
}
}
return nm;
}
int main()
{
ll i,j,k;
while(scanf("%lld %lld",&n,&m)!=EOF)
{
ll a,b,c;
memset(g,0,sizeof(g));
for(i=0;i<n;i++)
{
scanf("%lld %lld %lld",&a,&b,&c);
g[a][b]+=c;
}
printf("%lld\n",dinic());
}
return 0;
}