最大流EdmondsKarp算法

转载请注明出处:http://blog.csdn.net/Bule_Zst/article/details/78329115


问题阐述:

告诉你n个节点,以及m条边(有向边),已知每条边的容量和当前流量,求从第一个节点到第n个节点的最大流量。

求法:

根据已知的容量和流量可以计算出每条边目前还可以通过的流量,然后宽搜出一条从1到n的路(还可通过的流量必须大于0才认为这条边存在),找到这条路上最小的可通过的流量,然后更新答案以及这些边上的流量。循环操作直到不存在一条路从1到n。

这样做有一个问题,那就是,如果有多条路从1到达n,而我们可以选择其中的一些路来计算流量,那么如何选取就很重要。

举个例子

有4个结点,5条路,默认当前流量都为0,如果不为0,只需要把容量相应减少即可
起点,终点,容量
1 2 1
1 3 3
2 3 2
2 4 3
3 4 1

如果搜索到1-2-3-4这条路,然后将相应边更新,就会发现,不存在其他的从1到n的路了,然而答案显然不是1,因为1-3-4与1-2-4可以组成流量2。

因此,EK算法引入了一个错误纠正机制,正如上面的例子,其实找到1-2-3-4这条路是错误的,不应该选择这条路,那么怎么办呢,更新1-2-3-4这些边的同时,加入一些反向边,1-2-3-4这条路,把每条边的容量减1的同时,让反向边的容量加1,这样做之后,再去搜索从1到n的路就会发现多出来了一条,1-3-2-4,这样流量就变成2了。

走1-3-2-4这条路的本质就是:先走1-2-3-4这条路,然后发现出错了,因此不走1-2-3-4这条路,转而走1-2-4与1-3-4这两条路,走3-2这条边其实就是把之前的1-2-3-4这条路断开来,剩下1-2与3-4,然后与1-3-2-4中的1-3、2-4组合,形成新的路:1-2-4、1-3-4。

HDU-1532代码:

// @Team    : nupt2017team16
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;

#define LL          long long
#define MOD         1000000007
#define MEM(a,x)    memset(a,x,sizeof(a))
#define FILL(a,n,x) fill(a,a+n,x)
#define INF         0x3f3f3f3f
#define pb          push_back
#define FOR(i,a,b)  for( int i = ( a ); i <= ( b ); ++i )
#define WHILE()     int T;scanf( "%d", &T );while( T-- )

const int N = 200+7;

int n, m;

int C[N][N];
int pre[N];
bool vis[N];

bool bfs() {
    MEM( pre, -1 );
    pre[1] = 1;
    queue<int> q;
    q.push( 1 );
    MEM( vis, false );
    vis[1] = true;
    while( !q.empty() ) {
        int u = q.front();
        q.pop();
        FOR( i, 1, n ) {
            if( C[u][i] > 0 && vis[i] != true ) {
                q.push( i );
                vis[i] = true;
                pre[i] = u;
                if( i == n ) {
                    return true;
                }
            }
        }
    }
    return false;
}

int solve() {
    int ans = 0;
    while( bfs() ) {
        int mins = INF;
        for( int i = n; i != 1; i = pre[i] ) {
            mins = min( mins, C[pre[i]][i] );
        }
        for( int i = n; i != 1; i = pre[i] ) {
            C[pre[i]][i] -= mins;
            C[i][pre[i]] += mins;
        }
        ans += mins;
    }
    return ans;
}



int main()
{
    // freopen( "1532.txt", "r", stdin );
    while( scanf( "%d%d", &m, &n ) != EOF ) {
        MEM( C, 0 );
        int u, v, w;
        FOR( i, 0, m-1 ) {
            scanf( "%d%d%d", &u, &v, &w );
            C[u][v] += w;
        }
        printf( "%d\n",solve() );
    }
    return 0;
}

参考文章:http://blog.sina.com.cn/s/blog_6cf509db0100uy5n.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值