hdu 1532 Drainage Ditches && hdu 3549 Flow Problem 网络流最大流问题 Edmonds-Karp算法

本文详细介绍了EK算法在网络流问题中的应用,包括如何通过不断寻找增广路径来求解最大流问题,并给出了两个具体实例的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看了好几天最大流的EK算法了,到今天才总算想明白,趁热打铁写个总结巩固一下。

网络流中的最大流问题指的是给你一个网络,让你求出这个网络中从源点到汇点的最大流量

可以将其看成管道,每个管道有一个容量和流量,同时有一个残余流量的概念。

比如,设有一条边 u->v ;用 c [u,v] 表示这条边的容量,f [u,v] 表示这条路的流量,则这条边的残余流量 r [u,v] = c [u,v] - f [u,v] ;

EK算法的核心,就是不断地寻找增广路。这里的增广路指的是从源点到汇点的一条可行路,即其中每条边的残余流量均大于0;

当不能再找到增广路时,算法结束,此时源点到汇点之间再没有可行路了,显然流量已经达到了最大值。

通俗一点讲,就是再没有一条路从源点到汇点了,那么自然没有更多流量可以流入到汇点了。

算法的基本思路:通过BFS寻找增广路,每次寻找一条并由此更新整个网络中的残余流量,直至无法找到增广路为止。

hdu 的1532 和 3549 都是模板题,可以拿来练手,附上代码,有注释帮助理解。

hdu 1532
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define S 210
#define maxn 0xfffffff
int c[S][S];                                                      //这里表示残余流量
int pre[S],flow[S];                                               //前驱、到某个点的流量
int N,M;                                                          //此题中 M 即为汇点
queue<int>q;
int bfs()
{
    memset(pre,-1,sizeof(pre));
    while(!q.empty()) q.pop();
    flow[1]=maxn;                                                 //源点视为无限流出
    q.push(1);                                                    //源点入列
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        if(now==M) break;                                         //当前点为汇点时意味着寻找增广路结束
        for(int i=1;i<=M;i++)
        {
            if(i!=1&&c[now][i]>0&&pre[i]==-1)                     //回流到源点无意义,此题中 1 为源点
            {                                                     //残余流量必须大于0,同时前驱要求为-1,因为算法每次只寻找一条增广路
                pre[i]=now;                                       //若该点前驱不为-1意味着已经找到了一条从某个点到该点的增广路,
                flow[i]=min(flow[now],c[now][i]);                 //寻找增广路中允许通过的最大流量
                q.push(i);                                        //该点入列
            }
        }
    }
    if(pre[M]==-1) return -1;                                     //没有前驱意味着没有找到可行的增广路,返回-1
    return flow[M];
}
int maxflow()
{
    int add=0,sum=0;
    while((add=bfs())!=-1)                                        //add为增广路中可增加的流量,即该条增广路允许的最大流量
    {
        int k=M;
        while(k!=1)                                               //1为源点,利用前驱更新增广路中的残余流量
        {
            c[pre[k]][k]-=add;
            c[k][pre[k]]+=add;                                    //反向边
            k=pre[k];
        }
        sum+=add;
    }
    return sum;
}
int main()
{
    while(~scanf("%d%d",&N,&M))
    {
        memset(c,0,sizeof(c));
        memset(flow,0,sizeof(flow));
        int from,to,cap;
        for(int i=0;i<N;i++)
        {
            scanf("%d%d%d",&from,&to,&cap);
            c[from][to]+=cap;                                     //注意可能有重边,多条从某个点到另一点的边可将它们相加
        }
        cout<<maxflow()<<endl;
    }
}

hdu 3549
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define S 20
int N,M,c[S][S],flow[S],pre[S];
queue<int>q;
int bfs()
{
    while(!q.empty()) q.pop();
    memset(pre,-1,sizeof(pre));
    flow[1]=0xfffffff;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        if(now==N) break;
        for(int i=1;i<=N;i++)
        {
            if(i!=1&&c[now][i]>0&&pre[i]==-1)
            {
                pre[i]=now;
                flow[i]=min(flow[now],c[now][i]);
                q.push(i);
            }
        }
    }
    if(pre[N]==-1) return -1;
    return flow[N];
}
int maxflow()
{
    int sum=0,add=0;
    while((add=bfs())!=-1)
    {
        int k=N;
        while(k!=1)
        {
            c[pre[k]][k]-=add;
            c[k][pre[k]]+=add;
            k=pre[k];
        }
        sum+=add;
    }
    return sum;
}
int main()
{
    int T;
    cin>>T;
    int K=0;
    while(T--)
    {
        scanf("%d%d",&N,&M);
        memset(c,0,sizeof(c));
        memset(flow,0,sizeof(flow));
        int x,y,cap;
        for(int i=0;i<M;i++)
        {
            scanf("%d%d%d",&x,&y,&cap);
            c[x][y]+=cap;
        }
        printf("Case %d: %d\n",++K,maxflow());
    }
}


关于其中反向边的问题,记得看到过一句话:反向边就是给程序一个反悔的机会。

推荐一个博客,可供参考:传送门


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值