网络流最大流算法
网络流是图论里面最难,也只最有有魅力的分支,事实上图论中很多问题都可以转化为求解最大流的问题,比如说求解最小割的问题,使用网络流算法会使得求解最小割的时间复杂度降低很多,并且网络流算法还具有很大的普适性。
这里我就不多介绍网络流是什么了,相信关于网络流的具体模型,定义有很多科普文章进行了详细的介绍,这里只是给出两种具体的实现算法,以及思想原理。
Edmonds-Krap算法
该算法是一种在网络流残余图上寻找增光路的算法,寻找增广路的过程其实就是DFS或者BFS从源点搜索图都可以,但是关键是我们要记录一个增光路的路径。但这两者还是有一定区别的,使用DFS算法可能出现迭代收敛很慢的情况,而使用BFS我们可以确定至多搜索nm次。这样算法的时间复杂度上界就有了保证。
使用BFS算法我们可以使用一个前驱数组来保存搜索过程中每条边的父亲节点,对于边上权值得更改,这里有两种方法,①一种是建立边的哈希映射,这样我们只需要找到边的两个端点,即可通过哈希映射修改边的权值;②这里还有一种更为巧妙的做法,就是我们在建立领接链表时如果采用兄弟链表法,那么我们就可以使用成对变换的技巧,此时,我们在设置节点v的前驱节点时,我们只需要保存当前遍历的边的序号即可,通过成对变换我们可以寻找到这条边的另一个端点,并且还可以对这两条边进行更新。
实际上算法也就值包括两部分,一部分就是寻找一个条增广路,第二部分就是根据前驱数组更新边权,还有一个小细节就是我们在寻找增广路的过程中要记得记录增光路的最大权值。
在边缘都是整数的情况下,该算法的时间复杂度是O(nmm)的,上面我们已经提到了,BFS时,最多有搜索nm次,这是因为,每一次搜索增光路时,至少会让一条关键边的方向转变(增光路每次都减少最大流),而一个关键边至多成为关键边n次,那么总的可能就是nm次,每次搜索的时间复杂度是O(m+n),通常n<m成立,所以总的时间复杂度为O(nmm)。
例题
#include<bits/stdc++.h>
using namespace std;
//最大网络流算法
//Edmonds_Krap算法
//BFS寻找增光路的算法
//由于要更新正反向边的容量,所以这里使用成对变换的技巧
const int N=10010;
int edge[N];
int nest[N];
int val[N];
int last[N];
int cnt=2;//由于要使用成对变换的技巧,这里需要边从2开始编号
void add(int u,int v,int w){
edge[cnt]=v;
val[cnt]=w;
nest[cnt]=last[u];
last[u]=cnt;
cnt++;
return;
}
int pre[N];//记录增广路
int vise[N];//用来记录互斥访问
int s,t;//全局的源点和汇点
int flow=INT_MAX;
//BFS寻找最大流
bool bfs(){
memset(vise,false,sizeof(vise));
queue<int> que;
que.push(s);
vise[s]=true;
flow=INT_MAX;
for