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

看了好几天最大流的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());
    }
}


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

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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生就业服务平台管理系统按照操作主体分为管理员和用户。管理员的功能包括学生档案管理、字典管理、试卷管理、试卷选题管理、试题表管理、考试记录表管理、答题详情表管理、错题表管理、法律法规管理、法律法规收藏管理、法律法规留言管理、就业分析管理、论坛管理、企业管理、简历管理、老师管理、简历投递管理、新闻资讯管理、新闻资讯收藏管理、新闻资讯留言管理、学生信息管理、宣传管理、学生管理、职位招聘管理、职位收藏管理、招聘咨询管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 大学生就业服务平台管理系统可以提高大学生就业服务平台信息管理问题的解决效率,优化大学生就业服务平台信息处理程,保证大学生就业服务平台信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理新闻信息,管理大学生就业服务平台信息,包括考试管理,培训管理,投递管理,薪资管理等,可以管理新闻信息。 考试管理界面,管理员在考试管理界面中可以对界面中显示,可以对考试信息的考试状态进行查看,可以添加新的考试信息等。投递管理界面,管理员在投递管理界面中查看投递种类信息,投递描述信息,新增投递信息等。新闻信息管理界面,管理员在新闻信息管理界面中新增新闻信息,可以删除新闻信息。新闻信息类型管理界面,管理员在新闻信息类型管理界面查看新闻信息的工作状态,可以对新闻信息的数据进行导出,可以添加新新闻信息的信息,可以编辑新闻信息信息,删除新闻信息信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值