最大流EK算法

给定一个有向图G=(V,E),把图中的边看作
管道,每条边上有一个权值,表示该管道
的流量上限。给定源点s和汇点t,现在假设
在s处有一个水源,t处有一个蓄水池,问从
s到t的最大水流量是多少?
1

网络流图里,源点流出的量,等于汇点流
入的量,除源汇外的任何点,其流入量之
和等于流出两之和。
下面我们来考虑如何求最大流。
首先,假如所有边上的流量都没有超过容量(水管),那么就把这个流,称为一个可行流。易见,任一网络中都有一个零流,即每弧a上f(a)=0的流f.

我们就从这个零流开始考虑,假如有这么一条路,这条路从源点开始一直一段一段的连到了汇点(这条路叫做可行路径),并且,这条路上的每一段都满足流量<容量,注意,是严格的<,而不是<=。那么,我们一定能找到这条路上的每一段的(容量-流量)的值当中的最小值delta。我们把这条路上每一段的流量都加上这个delta,一定可以保证这个流依然是可行流,这是显然的。
这样我们就得到了一个更大的流,他的流量是之前的流量+delta,而这条路就叫做增广路。我们不断地从起点开始寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路为止。当找不到增广路的时候,当前的流量就是最大流。但这个想法是否正确?
2

考虑上面这样的图,如果我们沿着s-a-b-t路线走仅能得到一个100的流,实际上此图存在流量为200的流(sat+abt).问题出在过早地认为边a → b上流量不为0,因而“封锁”了流量继续增大的可能。

一种解决方法是在第一次找到增广路之后,在把路上每一段的容量减少delta的同时,也把每一段上的反方向的容量增加delta。即在Dec(c[x,y],delta)的同时,inc(c[y,x],delta)
第一次找到增广路: 3
第一次找到增光路添加反向边后得到的新图:
4
这样我们第二次搜索的时候就可以在新的网络里找到新的路径这是一个取消流的操作也可以看作是两条路径的合并。

5

第二次搜索又找到了一个流量为100的流,加上第一次搜索得到的流量为100的流,总流量上升到200

6

再对第二次搜索后的图添加反向边,变成:
7
在此图上再次进行dfs,已经找不到路径了,所以流量无法再增加,最大流就是200

在找增广路的时候采用广度优先的思想,我们就叫它EdmondsKarp算法。他是Ford&Fulkerson算法的改进。下面以一个题目为例给出代码:
poj1273
题目大意:
现在有m个池塘(从1到m开始编号,1为源点,m为汇点),及n条水渠,给出这n条水渠所连接的池塘和所能流过的水量,求水渠中所能流过的水的最大容量.
输入:
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
输出:
50

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
const int inf=0x7fffffff;
int r[maxn][maxn]; //残留网络,初始化为原图
bool visit[maxn];
int pre[maxn];
int m,n;
bool bfs(int s,int t)  //寻找一条从s到t的增广路,若找到返回true
{
    int p;
    queue<int > q;
    memset(pre,-1,sizeof(pre));
    memset(visit,false,sizeof(visit));
    pre[s]=s;
    visit[s]=true;
    q.push(s);
    while(!q.empty())
    {
        p=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(r[p][i]>0&&!visit[i])
            {
                pre[i]=p;
                visit[i]=true;
                if(i==t) return true;
                q.push(i);
            }
        }
    }
    return false;
}
int EdmondsKarp(int s,int t)
{
   int flow=0,d,i;
   while(bfs(s,t))
   {
       d=inf;
       for(i=t;i!=s;i=pre[i])
           d=d<r[pre[i]][i]? d:r[pre[i]][i];
       for(i=t;i!=s;i=pre[i])
       {
           r[pre[i]][i]-=d;
           r[i][pre[i]]+=d;
       }
       flow+=d;
   }
   return flow;
}

int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        int u,v,w;
        memset(r,0,sizeof(r));///
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            r[u][v]+=w;
        }
        printf("%d\n",EdmondsKarp(1,n));
    }
    return 0;
}

posted on 2017-12-14 19:56  Beserious  阅读(5272)  评论(0编辑  收藏  举报