Dinic 算法:
由于BFS寻找终点太慢,但是DFS又不能保证找到最短路径,当然,如果中和一下,构造分层网络的方法就可以找到比较快的最短增广路,(阻塞流算法)
dinic与EK的区别之处在于,EK是用BFS去找增广路,而dinic是在层次图里用DFS找增广路。
基本思路:
1.根据残量网络计算层次图;
2.在层次图中使用DFS进行增广直到不存在增广路;
3.不断重复直到无法增广的时候,算法结束。
所谓的层次图,在残量网络中从源点S起始进行BFS,这样每个顶点在BFS树中就会得到一个距源点s的距离d , 如果d(s) = 0,直接从s出发可以到达的点距离为1,下一层距离为2,,,,把具有相同距离的顶点放在同一层,在层次图中,只保留d(i) + 1 = d(j) 的边,这样在层次图中的任意路径就成为到达次顶点的最短路径。
在Dinic算法中,BFS的作用就是判断找的汇点是否在源点开始的层次图中。
接下来的问题是 如何在层次图中使用DFS进行增广?
如果找到一个节点在层次图中,用它往下延伸,一直延伸到汇点,如果存在,就在这条路上找最小容量,增广之;如果增广之后,
如果增广后,容量满了,阻塞之,实现方法即把这个点在层次图里的编号赋为其它点到不了的值,比如INT_MAX,-1。
如果不能到达汇点,这个节点返回上一层,继续找。
如果这个点都找完后(即上一层到达源点),再BFS从源点建立新的层次图,继续找。。。直到汇点不在新的层次图里结束。
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;
#define inf 0x3f3f3f3f
int tab[250][250];//邻接矩阵
int dis[250];//距源点距离,分层图
int N,M,ANS;//N:点数;M,边数
queue<int> Q;
int BFS()
{
int u,v;
memset(dis,-1,sizeof(dis));//以-1填充
dis[1]=0;
Q.push(1);
while(!Q.empty()){
u=Q.front(); Q.pop();
for(v = 1;v <= N;v ++)
if(tab[u][v] && dis[v] == -1){
dis[v] = dis[u] + 1;
Q.push(v);
}
}
return dis[N]>0; //汇点的DIS小于零,表明BFS不到汇点
}
//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广
int find(int x,int low)//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量
{
int i,a=0;
if (x==N)return low;//是汇点
for (i=1;i<=N;i++)
if (tab[x][i] >0 //联通
&& dis[i]==dis[x]+1 //是分层图的下一层
&&(a=find(i,min(low,tab[x][i]))))//能到汇点(a <> 0)
{
tab[x][i]-=a;
tab[i][x]+=a;
return a;
}
return 0;
}
int main()
{
int i,j,f,t,flow,tans;
while (scanf("%d%d",&M,&N)!=EOF){
memset(tab,0,sizeof(tab));
for (i=1;i<=M;i++)
{
scanf("%d%d%d",&f,&t,&flow);
tab[f][t]+=flow;
}
ANS=0;
while (BFS())//要不停地建立分层图,如果BFS不到汇点才结束
{
while(tans=find(1,inf))
ANS+=tans;//一次BFS要不停地找增广路,直到找不到为止
}
printf("%d\n",ANS);
}
return 0;
}