C++ 【蓝书】网络流问题(例题HDU 1532+三个算法+做题时的选择+模板整合)【网络流从入门到放弃】_c++网络流-程序员宅基地

技术标签: 算法  C++  网络流问题  【网络流从入门到放弃】  (例题HDU 1532+三个算法+做题时的选  【蓝书】  

关于网络流概念问题可以参考这篇博客:
C++ 【蓝书】网络流问题(网络流基础概念+三个算法+做题时的选择+模板整合)【网络流从入门到放弃】

HDU 1532原题地址
Problem 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

分析
给出了关于网络流问题的三大经典算法,EK算法,FF算法,Dinic算法
可以往下详细看提交的图片就能知道EK算法速度比较慢 推荐一般情况下用Dinic算法

Dinic算法,可以看作是两种方法的结合体,它进行了一定的优化,对于某些横边多的图,运行速度方面得到了大幅提升

Dinic算法的基本思路:
①根据残量网络计算层次图。

②在层次图中使用DFS进行增广直到不存在增广路

③重复以上步骤直到无法增广

层次图:分层图,以[从原点到某点的最短距离]分层的图,距离相等的为一层,

观察前面的dfs算法,对于层次相同的边,会经过多次重复运算,很浪费时间,那么,可以考虑先对原图分好层产生新的层次图,即保存了每个点的层次,注意,很多人会把这里的边的最大容量跟以前算最短路时的那个权值混淆,其实这里每个点之间的距离都可以看作单位距离,然后对新图进行dfs,这时的dfs就非常有层次感,有筛选感了,同层次的点不可能在同一跳路径中,直接排除。那么运行速度就会快很多了。


EK算法模板

#include <iostream>
#include <queue>
#include<string.h>
using namespace std;
#define arraysize 201
int maxData = 0x7fffffff;
int capacity[arraysize][arraysize]; //记录残留网络的容量
int flow[arraysize];                //标记从源点到当前节点实际还剩多少流量可用
int pre[arraysize];                 //标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
int n,m;
queue<int> myqueue;
int BFS(int src,int des)
{
    
    int i,j;
    while(!myqueue.empty())       //队列清空
        myqueue.pop();
    for(i=1;i<m+1;++i)
    {
    
        pre[i]=-1;
    }
    pre[src]=0;
    flow[src]= maxData;
    myqueue.push(src);
    while(!myqueue.empty())
    {
    
        int index = myqueue.front();
        myqueue.pop();
        if(index == des)            //找到了增广路径
            break;
        for(i=1;i<m+1;++i)
        {
    
            if(i!=src && capacity[index][i]>0 && pre[i]==-1)
            {
    
                 pre[i] = index; //记录前驱
                 flow[i] = min(capacity[index][i],flow[index]);   //关键:迭代的找到增量
                 myqueue.push(i);
            }
        }
    }
    if(pre[des]==-1)      //残留图中不再存在增广路径
        return -1;
    else
        return flow[des];
}
int maxFlow(int src,int des)
{
    
    int increasement= 0;
    int sumflow = 0;
    while((increasement=BFS(src,des))!=-1)
    {
    
         int k = des;          //利用前驱寻找路径
         while(k!=src)
         {
    
              int last = pre[k];
              capacity[last][k] -= increasement; //改变正向边的容量
              capacity[k][last] += increasement; //改变反向边的容量
              k = last;
         }
         sumflow += increasement;
    }
    return sumflow;
}
int main()
{
    
    int i,j;
    int start,end,ci;
    while(cin>>n>>m)
    {
    
        memset(capacity,0,sizeof(capacity));
        memset(flow,0,sizeof(flow));
        for(i=0;i<n;++i)
        {
    
            cin>>start>>end>>ci;
            if(start == end)               //考虑起点终点相同的情况
               continue;
            capacity[start][end] +=ci;     //此处注意可能出现多条同一起点终点的情况
        }
        cout<<maxFlow(1,m)<<endl;
    }
    return 0;
}

EK算法写的本题模板
在这里插入图片描述

#include <cstdio>
#include <algorithm>
#include <queue>
#include <string.h>
using namespace std;
int const MAX = 1005;
int const inf = 0x3f3f3f3f;
int c[MAX][MAX];//c[u][v]保存容量
int f[MAX][MAX];//f[u][v]保存当前流量
int a[MAX];// a数组在每趟bfs中找到最小路径中最小残余流量的,a数组使个递推数组,a[v]的意思是从源点s到点v的最小残余流量
int p[MAX];//保存前一个点
int n, m;
int bfs(int s, int t)
{
    
    queue<int> q;
    int flow = 0;
    while(!q.empty())   q.pop();
    memset(f, 0, sizeof(f));
    while(1){
    
        memset(a, 0, sizeof(a));
        a[s] = inf;//将起始点的最小残余量设为最大
        q.push(s);
        while(!q.empty()){
    //bfs找到一条最短路,这里的边不代表距离,可以看作每两个点都是单位距离的
            int u;
            u = q.front();
            q.pop();
            for(int v = 1; v <= m; v++){
    //枚举所有点v <u,v>
                if(!a[v] && c[u][v] > f[u][v]){
    //a[]可以代替vis[],来判断这个点是否已经遍历过,后面那个条件更是起了关键作用,很巧妙
                    p[v] = u;
                    q.push(v);
                    a[v] = min(a[u], c[u][v] - f[u][v]);//递推
                }
            }
        }
        if(!a[t])   break;//直到最小残余流量为0时,退出
        for(int u = t; u != s; u = p[u]){
    
            f[p[u]][u] += a[t];
            f[u][p[u]] -= a[t];
        }
        flow += a[t];
    }
    return flow;
}

int main()
{
    
    while(~scanf("%d %d", &n, &m)){
    
        memset(c, 0, sizeof(c));
        memset(p, 0, sizeof(p));
        for(int i = 1; i <= n; i++){
    
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            c[u][v] += w;
        }
        printf("%d\n", bfs(1, m));
    }
    return 0;
}


FF算法模板

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int map[300][300];
int used[300];
int n,m;
const int INF = 1000000000;
int dfs(int s,int t,int f)
{
    
    if(s == t) return f;
    for(int i = 1 ; i <= n ; i ++) {
    
        if(map[s][i] > 0 && !used[i]) {
    
            used[i] = true;
            int d = dfs(i,t,min(f,map[s][i]));
            if(d > 0) {
    
                map[s][i] -= d;
                map[i][s] += d;
                return d;
            }
        }
    }
}
int maxflow(int s,int t)
{
    
    int flow = 0;
    while(true) {
    
        memset(used,0,sizeof(used));
        int f = dfs(s,t,INF);//不断找从s到t的增广路
        if(f == 0) return flow;//找不到了就回去
        flow += f;//找到一个流量f的路
    }
}
int main()
{
    
    while(scanf("%d%d",&m,&n) != EOF) {
    
        memset(map,0,sizeof(map));
        for(int i = 0 ; i < m ; i ++) {
    
            int from,to,cap;
            scanf("%d%d%d",&from,&to,&cap);
            map[from][to] += cap;
        }
        cout << maxflow(1,n) << endl;
    }
    return 0;
}

用vector写的Ford-Fulkerson算法的本题代码
在这里插入图片描述

#include <cstdio>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
int const inf = 0x3f3f3f3f;
int const MAX = 300;
struct Node
{
    
    int to;  //与这个点相连的点
    int cap; //以这个射出的边的容量
    int rev; //这个点的反向边
};
vector<Node> v[MAX];
bool used[MAX];

void add_node(int from, int to, int cap)//重边情况不影响
{
    
    v[from].push_back((Node){
    to, cap, v[to].size()});
    v[to].push_back((Node){
    from, 0, v[from].size() - 1});
}
int dfs(int s, int t, int f)
{
    
    if(s == t)
        return f;
    used[s] = true;
    for(int i = 0; i < v[s].size(); i++){
    
        Node &tmp = v[s][i];
        if(used[tmp.to] == false && tmp.cap > 0){
    
            int d = dfs(tmp.to, t, min(f, tmp.cap));
            if(d > 0){
    
                tmp.cap -= d;
                v[tmp.to][tmp.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t)
{
    
    int flow = 0;
    while(1){
    
        memset(used, false, sizeof(used));
        int f = dfs(s, t, inf);
        if(f == 0)
            return flow;
        flow += f;
    }
    return flow;
}
int main()
{
    
    int n, m;
    while(scanf("%d %d", &n, &m) != EOF){
    
        for(int i = 0; i <= m; i++)
            v[i].clear();
        int u1, v1, w;
        for(int i = 1; i <= n; i++){
    
            scanf("%d %d %d", &u1, &v1, &w);
            add_node(u1, v1, w);
        }
        printf("%d\n", max_flow(1, m));
    }
    return 0;
}


Dinic算法
在这里插入图片描述

#include <cstdio>
#include <string.h>
#include <queue>
using namespace std;
int const inf = 0x3f3f3f3f;
int const MAX = 205;
int n, m;
int c[MAX][MAX], dep[MAX];//dep[MAX]代表当前层数

int bfs(int s, int t)//重新建图,按层次建图
{
    
    queue<int> q;
    while(!q.empty())
        q.pop();
    memset(dep, -1, sizeof(dep));
    dep[s] = 0;
    q.push(s);
    while(!q.empty()){
    
        int u = q.front();
        q.pop();
        for(int v = 1; v <= m; v++){
    
            if(c[u][v] > 0 && dep[v] == -1){
    //如果可以到达且还没有访问,可以到达的条件是剩余容量大于0,没有访问的条件是当前层数还未知
                dep[v] = dep[u] + 1;
                q.push(v);
            }
        }
    }
    return dep[t] != -1;
}

int dfs(int u, int mi, int t)//查找路径上的最小流量
{
    
    if(u == t)
        return mi;
    int tmp;
    for(int v = 1; v <= m; v++){
    
        if(c[u][v] > 0 && dep[v] == dep[u] + 1  && (tmp = dfs(v, min(mi, c[u][v]), t))){
    
            c[u][v] -= tmp;
            c[v][u] += tmp;
            return tmp;
        }
    }
    return 0;
}

int dinic()
{
    
    int ans = 0, tmp;
    while(bfs(1, m)){
    
        while(1){
    
            tmp = dfs(1, inf, m);
            if(tmp == 0)
                break;
            ans += tmp;
        }
    }
    return ans;
}

int main()
{
    
    while(~scanf("%d %d", &n, &m)){
    
        memset(c, 0, sizeof(c));
        int u, v, w;
        while(n--){
    
            scanf("%d %d %d", &u, &v, &w);
            c[u][v] += w;
        }
        printf("%d\n", dinic());
    }
    return 0;
}


学如逆水行舟,不进则退

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42429718/article/details/99050634

智能推荐

计算机毕业设计Java疫情防控医用品管理(系统+源码+mysql数据库+Lw文档)_疫情防护用品销售管理系统 论文-程序员宅基地

文章浏览阅读467次。计算机毕业设计Java疫情防控医用品管理(系统+源码+mysql数据库+Lw文档)springboot基于SpringBoot的婚庆策划系统的设计与实现。JSP健身俱乐部网站设计与实现sqlserver和mysql。JSP网上测试系统的研究与设计sqlserver。ssm基于SpringMvC的流浪狗领养系统。ssm基于Vue.js的音乐播放器设计与实现。ssm校园流浪猫图鉴管理系统的设计与实现。_疫情防护用品销售管理系统 论文

android插件化开发打包,Android项目开发如何设计整体架构-程序员宅基地

文章浏览阅读988次,点赞28次,收藏28次。最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。由于篇幅有限,这里以图片的形式给大家展示一小部分。

基于单片机数码管秒表控制系统设计-程序员宅基地

文章浏览阅读600次,点赞11次,收藏6次。*单片机设计介绍,基于单片机数码管秒表控制系统设计。

Python小程序之验证码图片生成_小程序图片验证码后端生成-程序员宅基地

文章浏览阅读235次。python小程序之验证码图片的生成定义随机字母的生成函数定义随机颜色生成函数,采用RGB格式,生成一个元组调用Image,生成画布,填充底色为白色调用画笔函数Draw,传入画布对象填充画布的每一个色块,作为背景在画布上控制间距,填上每一个字在最后的图上进行模糊操作代码# 生成一个随机的二维码小程序from PIL import Image,ImageDraw,ImageF..._小程序图片验证码后端生成

思科自防御网络安全方案典型配置_思科设备怎么ranga)服务器区域独立防护;-程序员宅基地

文章浏览阅读2.2k次。 1. 用户需求分析客户规模:客户有一个总部,具有一定规模的园区网络; 一个分支机构,约有20-50名员工; 用户有很多移动办公用户 客户需求:组建安全可靠的总部和分支LAN和WAN; 总部和分支的终端需要提供安全防护,并实现网络准入控制,未来实现对VPN用户的网络准入检查; 需要提供IPSEC/SSLVPN接入; 在内部各主要部门间,及内外网络间进_思科设备怎么ranga)服务器区域独立防护;

苹果账号迁移流程_apple 账号迁移-程序员宅基地

文章浏览阅读445次。4、转移账号生成的 p8 文件(证书文件)1、转移苹果账号的 teamID。2、接受苹果账号的 teamID。5、接受账号生成的 p8 文件。3、转移应用的 AppID。_apple 账号迁移

随便推点

深度学习中优化方法之动量——momentum、Nesterov Momentum、AdaGrad、Adadelta、RMSprop、Adam_momentum seg-程序员宅基地

文章浏览阅读1k次。https://blog.csdn.net/u012328159/article/details/80311892_momentum seg

动态数据生成静态html页_监听数据变更自动生成静态html-程序员宅基地

文章浏览阅读816次。主要的原理就是替换模板里的特殊字符。 1、静态模板页面 template.html,主要是定义了一些特殊字符,用来被替换。 HTML code DOCTYPE HT_监听数据变更自动生成静态html

预防按钮的多次点击 恶意刷新-程序员宅基地

文章浏览阅读494次。 今日在做一个新闻系统的评论时. 想到了预防"提交"按钮的多次点击的问提 (prevent multiple clicks of a submit button in ASP.NET). 以前碰到此类问提总是用重定位页面来解决. 这次我想找到一个一劳永逸的办法. 通过查讯Google,找到了一些代码,挑选一些较好的修改了一下。public void pa

sokcs5软件dante配置指南_dante 代理 配置pam用户名密码 模式-程序员宅基地

文章浏览阅读4.7k次。近来公司业务有需要做socks5代理的需求,研究了一下,主要的开源实现有2个:dante http://www.inet.no/dante/ss5 http://ss5.sourceforge.net/比较了一下,还是比较倾向于dante,因为看到有人这样评价ss5:Project has an incredibly poor source code quality. Th_dante 代理 配置pam用户名密码 模式

Excel vba 求助。_vba countifs 源码-程序员宅基地

文章浏览阅读809次。在excel vba 中用到countifs 函数,但用来统计带有特殊符号* 时总是统计chu_vba countifs 源码

web前端基础——实现动画效果_web前端实现图片动画效果-程序员宅基地

文章浏览阅读2.6k次。当两个效果之间变换时,可以使用transition过渡属性,但是有多个效果来回变换时,就需要使用动画效果,且动画过程可控(重复播放,画面暂停,最终画面等)文章目录1、简介2、实现步骤3、复合属性animation4、动画属性1、简介动画的本质是快速切换大量图片在人脑中形成的具有连续性的画面构成动画的最小单元:帧或者动画帧2、实现步骤定义动画@keyframes 动画名称{ from{} to{}}@keyframes 动画名称{ 0%{} 10%{} 20%{} 50._web前端实现图片动画效果

推荐文章

热门文章

相关标签