最大流问题

参考链接: http://www.cppblog.com/mythit/archive/2009/04/19/80470.aspx
对最大流问题比较感性的认识,要看证明还是要看算法导论的相关章节。
最大流问题:
给定一个有向图,一般情况下边的值为整数,定义不直接相连的节点间的边值为0,如果有节点i和j直接由多条边,则将这些边合并为一条,值取和。则若i到j有边,则j到i的边为0,这些边称为反向边。定义其中的两个点位源点和汇点,则这个有向图可以视为流网络。网络中的有向边的值表示可以通过该边的最大流量,最大流问题就是求从源点出发,流进汇点的最大流量。
为了求最大流,必须了解残余网络,增广路径,反向弧等概念。
残余网络是针对流网络的某个状态而定义的。
如图是一个流网络的状态,其中1为源点,7为汇点,每天边的值为v/c的形式表示,v代表当前状态该边上的实际流量,c代表该边的最大流量:
最大流问题_第1张图片

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

最大流问题_第2张图片

以上方法为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上一到入门级的最大流问题:例题: http://poj.org/problem?id=1273

例题的代码实现,基本仿照参考链接:
说明:
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]



你可能感兴趣的:(算法,C++基础)