对最大流问题比较感性的认识,要看证明还是要看算法导论的相关章节。

最大流问题:

给定一个有向图,一般情况下边的值为整数,定义不直接相连的节点间的边值为0,如果有节点i和j直接由多条边,则将这些边合并为一条,值取和。则若i到j有边,则j到i的边为0,这些边称为反向边。定义其中的两个点位源点和汇点,则这个有向图可以视为流网络。网络中的有向边的值表示可以通过该边的最大流量,最大流问题就是求从源点出发,流进汇点的最大流量。


为了求最大流,必须了解残余网络,增广路径,反向弧等概念。


残余网络是针对流网络的某个状态而定义的。


如图是一个流网络的状态,其中1为源点,7为汇点,每天边的值为v/c的形式表示,v代表当前状态该边上的实际流量,c代表该边的最大流量:



则它的残余网络就是从中找到一个从源点到汇点的路径(该路径中不存在值为0的边),选取这条路径中v的最小值minflow,也就是该路径的实际流量,然后将该路径的所有边的实际容量都更新为c(i,j)-minflow,并修改其反向边的值为c(j,i)+minflow(这一步的意义可能是直接的贪心求法无法得到最优解,需要增加方向边来修正错误?)。这样的一条路径称为增广路径,修改值后的流网络成为残留网络。增广路径的意义在于能给当前最大流增加多少的值,而残余网络的意义在于每个节点从源点的流入量还能增加多少,0表示不能再增加了。当残留网络中没有增光路经时,则当前流入汇点的流值为最大流。


下图是找到两条增光路经,并修改流网络后的残余网络(我感觉此时3到4,5到6和6到7之间应该仍然为0):


以上方法为Ford-Fulkerson方法,该方法的一种实现是基于BFS的Edmonds-Karp算法。



算法流程如下:


  设队列Q:存储当前未访问的节点,队首节点出队后,成为已检查的标点;


  Path数组:存储当前已访问过的节点的增广路径;


  Flow数组:存储一次BFS遍历之后流的可改进量;


  Repeat:


    Path清空;


    源点S进入Path和Q,Path[S]<-0,Flow[S]<-+∞;


    While Q非空 and 汇点T未访问 do


        Begin


            队首顶点u出对;


            For每一条从u出发的弧(u,v) do


                If v未访问 and 弧(u,v) 的流量可改进;


                Then Flow[v]<-min(Flow[u],c[u][v]) and v入队 and Path[v]<-u;


    End while


   


    If(汇点T已访问)


    Then 从汇点T沿着Path构造残余网络;


  Until 汇点T未被访问




POJ上一到入门级的最大流问题:例题​



例题的代码实现,基本仿照参考链接:


说明:


start:源点


terminal:汇点


map[LEN][LEN]:流网络


bfsqueue:每一状态中,用BFS最增广路经,每次只求一条增广路径


minflow[LEN]:记录从源点流入每个点的最大可能值,这个值也将该路径中的最小值(该路径的实际流量)一直传递到汇点。该值是每次BFS都要保留的


path[LEN]:记录增广路径,其中path[j] = i表示从节点 i 流向节点 j 。同时也记录了每次bfs时记录是否考察了当前节点。



每次BFS完后,查看path[terminal],如果path[terminal]==-1,则表示没有从源点到汇点的增广路径了。


每次BFS完后,要更新增广路径中的容量和方向路径的容量,因为minflow已经记录了增光路经中的最小值,用minflow[terminal]更新即可。



#define LEN 201
int map[LEN][LEN];
int minflow[LEN];
int n,m;
int start;
int terminal;
queue< int> bfsqueue;
int bfs(int path[LEN]){
memset(path,-1, sizeof (int )*LEN);
while (!bfsqueue.empty()) bfsqueue.pop();
path[start] = 0;
minflow[start] = MAXINT;
bfsqueue.push(start);
while (!bfsqueue.empty()){
int node = bfsqueue.front();
bfsqueue.pop();
for (int i=1;i<=m;++i){
if (path[i] == -1 && map[node][i]!=0 && i != start){
minflow[i] = (minflow[node]<map[node][i])?minflow[node]:map[node][i];
path[i] = node;
bfsqueue.push(i);
}
}
}
if (path[terminal] == -1) return -1;
return minflow[terminal];
}
int edmonds_karp(){
int max_flow = 0;
int path[LEN];
int increasedflow;
int node;
while ( (increasedflow=bfs(path)) != -1){
max_flow += increasedflow;
node = terminal;
while (node != start){
int pre = path[node];
map[pre][node] -= increasedflow;
map[node][pre] += increasedflow;
node = pre;
}
}
return max_flow;
}