【专题】网络流之最大流算法

网络流之最大流算法

AIDreamer

2017/4/29

  • 最大流问题

    在一个有向无环图中,用S和T来分别表示源点和汇点,每条边有一个权值 cij 代表容量限制,问从S最多可以流多少流量到T

    fij 表示每条边实际的流量,需要满足一下限制条件

    • 非负性:实际流量 fij0
    • 容量条件: fijcij
    • 流量守恒条件:每个节点流出的流量等于流入的流量(源点和汇点除外,源点的流入流量可以为任何值)
  • 最大流算法

    我们考虑从S到T的一条可行路径上的一条边 (u,v) ,设这条边的容量为 cuv 流过的流量为 fuv

    可以看出从u到v还可以流 cuvfuv 的流量过去,而从v到u可以流 fuv 的流量(因为正向流量和负向流量是可以相互抵消的),所以我们可以等价地将一条有流量流过的又有成是两条反向的又向边,并保证两条有向边的容量之和为总容量c

    cf={cuvfuvfvu(u,vEfuv<cuv(u,v)Efvu>0

    这里写图片描述

    我们将包含以上两类边地网络称作剩余网络

    算法流程如下:

    不断地在剩余网络中寻找从源点S到T的可行的(能够继续提高流量的)路径,直到找不到路径为止。

    该算法正确性的证明隐含在之后的最小分割最大流定理中

  • 分割的定义

    一个分割将所有节点分为两个不相交的集合L和R,并使S和T分别属于L和R,我们定义该分割的容量为L到R的所有边的容量之和(注意:分割是针对原图来说而不是剩余网络)。不难得出这样的结论:

    对于任意的流f和任意的分割(L,R), f(ST)(L,R)

  • 最小分割最大流定理

    网络中最大流的规模等于其中分割的最小流量

    简言之就是 =

    这里不做严格的证明,只给出直观的一个理解:

    从分割的定义我们可以知道 ,现在只需要证明等号始终能够取到。

    在之前的最大流算法流程中,我们是不断寻找从S到T的可行路径直到找不到路径为止,这个时候剩余网路中的点被分为两类点:可以到达的点和不可到达的点。我们将从S出发可以到达的所有点称为L集合,则R=V-L为剩下的节点集合,T包含在R中,于是 (L,R) 构成了图G的一个分割.也就是我们找到了一个等于该分割的流,由 我们可以知道该分割一定是最小分割,而该流也一定是最大流。

    不难发现,任意由L到R的边都是满流的,而任意由R到L的边的流量都是0.

  • 参考代码

    int mat[200][200],flow[200],path[200],Start,End;//mat[i][j]表示i和j之间的边,没有边值为0
    int Bfs()//通过广搜找一条从源点到汇点的路径,不能找到一条路径就返回-1
    {
        queue<int> Q;
        Q.push(Start);//将源点入队
    
        memset(path,-1,sizeof(path));//path[i]保存i的前驱节点也就是从拿个点走到i的,为-1表示未访问过节点i
        path[Start]=0;
        flow[Start]=INF;//flow[i]表示能够流到i的最大流量
        while(!Q.empty())
        {
              int t=Q.front();Q.pop();
              if(t==End)break;//如果搜到汇点(终点)则停止搜索
              for(int i=0;i<=n;i++)
              {
                    if(i!=Start && mat[t][i]!=0 && path[i]==-1)
                    {
                          flow[i]=min(flow[t],mat[t][i]);
                          Q.push(i);
                          path[i]=t;
                    }
              }
        }
        if(path[End]==-1)return -1;//如果终点始终没有访问到说明没有从源点出发到汇点的可行路径
        else return flow[End];//如果有可行路径则返回这条路径上的最大可行流量(最大可行流量也就是最小边权)
    }
    int Edmonds_Karp()
    {
        int max_flow=0,temp,now,pre;
        while((temp=Bfs())!=-1)
        {
             max_flow+=temp;//max_flow保存总的流量
             now=End;
             while(now!=Start)//从汇点反向地将可行路径上的边权更新
             {
                   pre=path[now];
                   mat[pre][now]-=temp;//减少正向边的流量
                   mat[now][pre]+=temp;//增加反向边的流量
                   now=pre;
             }
        }
        return max_flow;
    }
  • 网络流经典问题

    多源多汇问题:在有多个源点多个汇点的网络中求最大流量,解决方案是用一个超级源点和所有源点相连,再用一个超级汇点和所有汇点相连,这样就转化成了原始的单源单汇最大流问题了。

  • 例题POJ 1459 Power Network

    题目大意

    现在有发电站(最大能产生一定电量)、中继站(不产生也不消耗)、用户(最大消耗一定电量),用节点来表示,两个节点之间有传输通道最大能传输一定的电量,问最多能消耗的点量是多少。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值