首先是网络流问题的提出:
一个源点,一个汇点,不断又源点向汇点输送,其中路径上有最大速度,求汇点收货的最大速度是多少。
在求之前可以先模拟一下过程如果源点现在只能出一个单位的货,那么他会找一个未达到最大速度的路进行运输,接着我们不断将运输的量放大,即不断地从起点开始寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路为止。可知当找不到增广路的时候,当前的流量就是最大流。
那么像这种先扩张再找终点的情况可以用bfs来实现,即将点存在队列里。
但这样找到的只是先从这条路开始找所找到的最大流,故应该是他在发现不是最大时具备回溯的能力,但回溯搜索会使我们的效率就上升到指数级,因此我们引入反向边即每条边(i,j)都有一条反向边(j,i),反向边也同样有它的值,在把路上每一段的容量减少delta的同时,也把每一段上的反方向的容量增加delta。
这样方法就有了,就差构建了
变量定义
#define maxn 201
int maxData = 0x7fffffff;
int capacity[maxn][maxn]; //记录残留网络的值
int flow[maxn];//标记从源点到当前节点实际还剩多少流量可用
int pre[maxn];//标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
queue<int> ekqueue;
BFS搜索
通过搜索找出增广路
int BFS(int src,int des)//bfs用来找由src到des的最大值
{
int i,j;
while(!ekqueue.empty())//初始化队列为空
ekqueue.pop();
for(i=1;i<m+1;++i)//将前驱全部初始为-1
{
pre[i]=-1;
}
pre[src]=0;
flow[src]= maxData;//源点的值是无限大
ekqueue.push(src);//将源点入队
while(!ekqueue.empty())//BFS一直搜索到没有扩张点
{
int index = ekqueue.front();//将第一个点提到index
ekqueue.pop();//第一个点出队
if(index == des)//找到了增广路径
break;
for(i=1;i<m+1;++i)
{
if(i!=src && capacity[index][i]>0 && pre[i]==-1)//存在剩余网络且那个点不在队列中
{
pre[i] = index; //记录前驱
flow[i] = min(capacity[index][i],flow[index]);//关键:迭代的找到增量
ekqueue.push(i);
}
}
}
if(pre[des]==-1)//残留图中不再存在增广路径,即找到了一个最大流
return -1;
else
return flow[des];
}
接着我们将增广后的增量添加到总价值里
int maxFlow(int src,int des)//最大流
{
int increasement= 0;//增量
int sumflow = 0;//最终的最大流
while((increasement=BFS(src,des))!=-1)
{
int k = des; //利用前驱寻找路径
while(k!=src)
{
int last = pre[k];
capacity[last][k] -= increasement; //改变正向边的容量
capacity[k][last] += increasement; //改变反向边的容量
k = last;
}
sumflow += increasement;
}
return sumflow;
}
这样看可能还是不大好懂那么我们以一组样例为例:
“ 5 4 ”
“ 1 4 20 ”
“ 1 2 40 ”
“ 2 4 20 ”
” 2 3 30 ”
” 3 4 10 ”
对第一条边:
1. flow[1]=INF,pre[1]=0;
源点1进队列,开始找增广路,
capacity[1][2]=40>0,则flow[2]=min(flow[1],40)=40;
capacity[1][4]=20>0,则flow[4]=min(flow[1],20)=20;
capacity[2][3]=30>0,则flow[3]=min(folw[2]=40,30)=30;
capacity[2][4]=30,但是pre[4]=1(已经在capacity[1][4]这遍历过4号点了)
capacity[3][4].....
当index=4(汇点),结束增广路的寻找
传递回increasement(该路径的流),利用前驱pre寻找路径
这时由1到4出现了一条零路,由4到1的价值为20(反向边)
2.flow[1]=INF,pre[1]=0;
源点1进队列,开始找增广路,capacity[1][2]=40>0,则flow[2]=min(flow[1],40)=40;
capacity[1][4]=0!>0,跳过
capacity[2][3]=30>0,则flow[3]=min(folw[2]=40,30)=30;
capacity[2][4]=30,pre[4]=2,
则flow[2][4]=min(flow[2]=40,20)=20;
capacity[3][4].....
当index=4(汇点),结束增广路的寻找
传递回increasement(该路径的流),利用前驱pre寻找路径
一直这样搜下去
最终sumflow=20+20+10=50(这个就是最大流的值)