网络流(最大流)EK算法

最大流问题:

假设需要把一些物品从节点s(称为源点) 运送到节点t(称为汇点) 可以从其他点进行中转,每两个点之间都有容量的限制,问从s到t,能运送的最大值是多少。

上图 从 1-4 最大流为 80 + 50 = 130

刘汝佳白书定义:

对于一条边(u,v)他的物品上限称为容量,记c(u,v)(对于不存在的边,c(u,v) = 0 );实际运送的物品称为流量(flow) 记f(u,v)从u运送到3个物品至v,再从v运送5个物品至u,等价于从v送2个物品至u。可以规定f(u,v) = -f(v,u)

在最大流问题中,容量c与流量f需要满足3个条件:容量限制(c(u,v) >= f(u,v)),斜对称性(f(u,v) = -f(v,u)) 和流量平衡(对于除了节点s与t之外的任意节点u  西格玛\sum\sumf(u,t) = 0,这样是保证s->t 所有的通路都被占用,不会再有残量)即从s的流出净流量等于t的汇入净流量。

 

介绍完最大流问题后,我们介绍一下他的最基本的算法:增广路算法。

这里需要知道每边的容量与实际流量之差(称为残量),用残量构建一个网络(称为残量网络(residual network))。例如上述例子的“残量网络”为

但是仅此还不够,残量网络是包含正反两条边的,上面的图只是完成了一半。反边的画法就是原来的边的容量

此图才是残量网络。

为什么要加上反边呢?

我们来看一个新的案例

对于此网络,假设有一条路从 1-2-3-4

对于这种情况算出来的最大流就是1,但是我们很容易看出最大流应该是2。我们加上反边试试

加上反边后,存在一条路1-3-2-4的绿色路径

就算我们第一步走的1-2-3-4,之后又会走1-3-2-4 算出的最大流同样是2.

根据网络流的斜对称性 f(u,v) = -f(v,u)可知,我们2-3和3-2的净流量相当于0。实际上2-3之间的根本没有流量流过,我们的反边就是起到这个作用(后悔操作,也就是松弛)。。

我们的最大流就是不断的在残量网络中寻找增广路,并且该增广路的最大流量应该是该路的最小容量(容量限制法则)。 并且该增广路同时减去该最小容量,得到一个新的残量网络。最后残量网络无增广路(流量平衡原理),得出来的才是最大流。

 

下面看一道例题:

D - Drainage Ditches

Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Submit Status

Description

Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle. 

 

Input

The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch. 

 

Output

For each case, output a single integer, the maximum rate at which water may emptied from the pond. 

 

Sample Input

 

5 4

1 2 40

1 4 20

2 4 20

2 3 30

3 4 10

 

Sample Output

 

50

最大流的模板题,求1-n的最大流

直接看代码

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#define  LL long long
#define  ULL unsigned long long
#define mod 1000000007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
#define maxn 1000005
using namespace std;
const double eps = 1e-2;
double dp[205];
int n,m;
int capacity[205][205];//残量网络
int flow[205];//i的流量
int pre[205];//前驱节点,同时可以起到vis作用
int BFS(int st,int ed)//找增广路的最大流
{
    queue<int> Q;
    mem(pre,-1);
    pre[st] = 0;
    flow[st] = INF;
    Q.push(st);
    while(!Q.empty()){
      int v = Q.front();
      Q.pop();
      if(v == ed) break;
      for(int i = 1; i <= n; i++){
        if(i != st && capacity[v][i] > 0 && pre[i] == -1){
          pre[i] = v;
          flow[i] = min(capacity[v][i],flow[v]);
          Q.push(i);
        }
      }
    }
    if(pre[ed] == -1) return -1;
    else return flow[ed];

}
int maxFlow(int st,int ed)
{
    int increase = 0;
    int sumflow = 0;
    while((increase = BFS(st,ed)) != -1){//若存在增广路
      int k = ed;//从汇点像源点反过来找
      while(k != st){
        int last = pre[k];
        capacity[last][k] -= increase;//正向
        capacity[k][last] += increase;//反向
        k = last;
      }
      sumflow += increase;
    }
    return sumflow;
}
int main()
{
    while(~scanf("%d%d",&m,&n)){
      mem(flow,0);
      mem(capacity,0);
      for(int i = 0; i < m; i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(a == b) continue;
        capacity[a][b] += c;//防止重边出现
      }
      printf("%d\n",maxFlow(1,n));
    }




    return 0;

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值