您的位置:首页 > 其它

最大流

2015-12-26 18:44 295 查看

▶ 问题
在一个有向图里,每个路径都有最大容量,通过这个图最多能运输多少货物。默认容量非负,且不能同时存在边(u,v)和(v,u)。
▶ 基本思路
维护一个流网络,初始化为0,不断增加流的值。
维护一个残存网络(存储分配流后每条边剩余的容量),初始为最大容量。(在以下代码中,没有特别开一个数组存储残存网络,而是通过原容量减去流网络得到残存网络)
通过在残存网络里寻找增广路径(残存网络中从一条从源结点s到汇结点t的简单路径),并且根据这条路径的残存容量(能够为每条边增加的流量的最大值,也就是这条路径经过边的最小容量)来更新残存网络和流网络(在流网络里加上这个值,在残存网络里减去这个值),直到在残存网络中找不到增广路径为止。
在找不到增广路径时,流达到了稳定状态,也就是横跨任何切割的净流量相同,这个净割线的边流量的和为净流量,注意正向相加,反向相减)
▶ 代码实现
·基本变量
  int cap[MAX][MAX]; //记录每条路径的最大容量
  int flow[MAX][MAX]; //记录当前的流网络
  int a[MAX]; //记录当前路径的残存容量(最终的残余容量存储在a[t]中)
int path[MAX]; //路径压缩
int ans=0; //存储最大流
memset(cap,0,sizeof(cap));
memset(flow,0,sizeof(flow));

s代表源结点下标,t代表汇结点下标
  以下过程循环进行,直到残存网络不包括任何增广路径:

·循环结束条件

a[t]==0 ,其中t代表汇结点,残余容量仍旧是初始值0,说明残余容量数据没有更新到汇结点处,也就说明找不到增广路径。
if(a[t]==0) break;

·循环具体步骤

①利用宽度优先搜索,在残存网络里找到一条增广路径。
queue<int>q; //全局变量,bfs使用
memset(a,0,sizeof(a));
a[s]=INF; // 第3,4行操作对象是源结点,s代表源结点的下标值
q.push(s);
while( !q.empty() ){
u=q.front();q.pop();
for(v=0;v<n;v++){
if( !a[v] && cap[u][v]>flow[u][v]){
path[v] = u;
q.push(v);
a[v] = min(a[u],cap[u][v]-flow[u][v]);
}
}
}
②更新流网络
for(u=t;u!=s;u=path[u]){
flow[path[u]][u]+=a[t];
flow[u][path[u]]-=a[t];
}

③用当前路径残存容量更新最大流
ans+=a[t];

▶ 示例
  橙色路径为当前的增广路径,图里显示的是残存网络
第一轮循环:



残存容量:12,ans = 12;
第二轮循环:



残存容量:4,ans = 16;
第三轮循环:



残存容量:7,ans = 23;



找不到增广路径

最终最大流为23。

此时的稳定流网络:



检测各个切割:





净流量12+11 = 23
净流量 12+7+4 = 23





   净流量11+19-7 = 23 净流量19+4 = 23

▶ 具有多个源结点和汇点的网络

构造一个超级源结点和超级源汇点,让超级源结点指向所有源结点,添加一条容量为正无穷的有向边;让所有源汇点指向超级源汇点,添加一条容量为正无穷的有向边。
其余和普通最大流问题解决方法相同。

*《算法导论》中给出了存在反平行边的处理办法
*这里未引入反向边的概念,《算法导论》中的反向边就是这里的流网络。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: