经典网络流题目模板(P3376 + P2756 + P3381 : 最大流 + 二分图匹配 + 最小费用最大流)...

题目来源

  • P3376 【模板】网络最大流
  • P2756 飞行员配对方案问题
  • P3381 【模板】最小费用最大流

最大流

最大流问题是网络流的经典类型之一,用处广泛,个人认为网络流问题最具特点的操作就是建反向边,这样相当于给了反悔的机会,不断地求增广路的,最终得到最大流

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e6 + 3;
const int Max = 1e5 + 10;struct Edge {int to, next, flow;    //flow记录这条边当前的边残量
}edge[Max << 1];int n, m, s, t;
int head[Max], tot;
bool vis[Max];void init()
{memset(head, -1, sizeof(head));tot = 0;
}void add(int u, int v, int flow)
{edge[tot].to = v;edge[tot].flow = flow;edge[tot].next = head[u];head[u] = tot++;
}//向图中增加一条容量为exp的边(增广路)
int dfs(int u,int exp)
{if (u == t) return exp;            //到达汇点,当前水量全部注入vis[u] = true;                    //表示已经到了过了for(int i = head[u] ; i != -1  ;i = edge[i].next){int v = edge[i].to;if(!vis[v] && edge[i].flow > 0){int flow = dfs(v, min(exp, edge[i].flow));if(flow > 0)            //形成了增广路
            {edge[i].flow -= flow;edge[i ^ 1].flow += flow;return flow;}}}return 0;                        //无法形成增广路的情况
}//求最大流
int max_flow()
{int flow = 0;while(true){memset(vis, 0, sizeof(vis));int part_flow = dfs(s, inf);if (part_flow == 0) return flow;flow += part_flow;}
}int main()
{
#ifdef LOCALfreopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#endifwhile (scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){init();for (int i = 1, u, v, flow;i <= m; i++){scanf("%d%d%d", &u, &v, &flow);add(u, v, flow);add(v, u, 0);}printf("%d\n", max_flow());}return 0;
}
最简单算法-Ford-Fulkerson
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) "["<<x<<","<<y<<"]"
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e6 + 3;
const int Max = 1e5 + 10;struct Edge {int to, next, flow;    //flow记录这条边当前的边残量
}edge[Max << 1];int n, m, s, t;
int head[Max], tot;
int dis[Max];void init()
{memset(head, -1, sizeof(head));tot = 0;
}void add(int u, int v, int flow)
{edge[tot].to = v;edge[tot].flow = flow;edge[tot].next = head[u];head[u] = tot++;
}bool bfs() //判断图是否连通
{queue<int>q;memset(dis, -1, sizeof(dis));dis[s] = 0;q.push(s);while (!q.empty()){int u = q.front();q.pop();for (int i = head[u]; i != -1; i = edge[i].next){int v = edge[i].to;if (dis[v] == -1 && edge[i].flow > 0)        //可以借助边i到达新的结点
            {dis[v] = dis[u] + 1;                    //求顶点到源点的距离编号
                q.push(v);}}}return dis[t] != -1;                                //确认是否连通
}int dfs(int u, int flow_in)
{if (u == t) return flow_in;int flow_out = 0;                                    //记录这一点实际流出的流量for (int i = head[u]; i != -1;i = edge[i].next){int v = edge[i].to;if (dis[v] == dis[u] + 1 && edge[i].flow > 0){int flow_part = dfs(v, min(flow_in, edge[i].flow));if (flow_part == 0)continue;                //无法形成增广路flow_in -= flow_part;                        //流出了一部分,剩余可分配流入就减少了flow_out += flow_part;                        //记录这一点最大的流出
edge[i].flow -= flow_part;edge[i ^ 1].flow += flow_part;                //减少增广路上边的容量,增加其反向边的容量if (flow_in == 0)break;}}return flow_out;
}int max_flow()
{int sum = 0;while (bfs()){sum += dfs(s, inf);}return sum;
}int main() {
#ifdef LOCALfreopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#endifwhile (scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){init();for (int i = 1, u, v, flow;i <= m; i++){scanf("%d%d%d", &u, &v, &flow);add(u, v, flow);add(v, u, 0);}printf("%d\n", max_flow());}return 0;
}
常用且高效的算法-Dinic

 二分图匹配

要解决这类问题,我们需要先了解什么是二分图?

二分图:一个图中的所有顶点可以分为两个集合 V,K ,其实两个集合内部的点彼此之间无边,如下图所示:(蓝色的点和红色的点分属于两个集合V,K)

然后我们回到这个题目上来,这个题目求的是最大可出战人数,实际上就是在二分图中找到两个集合中的最大匹配数,这类问题我们称之为二分图最大匹配数问题

属于网络流经典题目之一,下面说明一下建图的过程

1)由源点向集合V中每个点建一条容量为1的边

2)对于V,K集合之间存在的边e,v 为V中的点,k为K中的点,我们建一条容量为1的边,方向为 v --> k 

3)由K中每个点向汇点建一条容量为1的边

当我们将图建好了后,我们求这个图的最大流,这个最大流即为二分图最大匹配数,下面展示一下建成的图:(S代表源点,T代表汇点,蓝色的边代表容量为1的边)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e6 + 3;
const int Max = 1e6 + 10;struct Edge
{int to, next, flow;
}edge[Max << 1];;int n, m, a, b, s, t;
int head[Max], tot;
int dis[Max];
int ans;
bool vis[Max];void init()
{memset(head, -1, sizeof(head));tot = 0;ans = 0;
}void add(int u, int v, int flow)
{edge[tot].to = v;edge[tot].flow = flow;edge[tot].next = head[u];head[u] = tot++;
}bool bfs()
{memset(dis, -1, sizeof(dis));dis[s] = 0;queue<int>q;q.push(s);while (!q.empty()){int u = q.front();q.pop();for (int i = head[u]; i != -1;i = edge[i].next){int v = edge[i].to;if (dis[v] == -1 && edge[i].flow > 0){dis[v] = dis[u] + 1;if (v == t) return true;q.push(v);}}}return false;
}int dfs(int u, int flow_in)
{if (u == t) return flow_in;int flow_out = 0;for (int i = head[u]; i != -1;i = edge[i].next){int v = edge[i].to;if (dis[v] == dis[u] + 1 && edge[i].flow > 0){int flow_part = dfs(v, min(flow_in, edge[i].flow));if (flow_part == 0) continue;flow_in -= flow_part;flow_out += flow_part;edge[i].flow -= flow_part;edge[i ^ 1].flow += flow_part;if (flow_in == 0)break;}}return flow_out;
}int max_val()
{int sum = 0;while (bfs()){sum += dfs(s, inf);}return sum;
}int main()
{
#ifdef LOCALfreopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#endifwhile (scanf("%d%d", &m, &n) != EOF){init();s = 0, t = n + 1;for (int i = 1;i <= m;i++){add(s, i, 1);add(i, s, 0);    //由源点向外籍飞行员建边
        }for (int i = m + 1; i <= n;i++){add(i, t, 1);add(t, i, 0);}while (scanf("%d%d", &a, &b) != EOF && a != -1 && b != -1){add(a, b, 1);add(b, a, 0);}printf("%d\n", max_val());for (int u = 1;u <= m;u++){for (int i = head[u]; i != -1;i = edge[i].next){if (edge[i].flow == 0 && edge[i].to != s && edge[i].to != t){printf("%d %d\n", u, edge[i].to);}}}}return 0;
}
飞行员配对方案-Dinic

 最小费用最大流

这类题目相比于最大流问题新增了每天边单位流量的价格,问在最大流的情况下求出最小的费用。

这类题目和最大流很想,不过也有不小区别,对于这类问题,我们为每条边建的反边的价格是每天边的相反数,如图

然后我们的算法也不再是Dinic算法了,而是用spfa或者dijkstra

#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const int Max = 5e3 + 10;struct Edge
{int to, rev;                    //rev记录反向边int flow, cost;;
};int n, m, k;
vector<Edge>edge[Max << 1];
int h[Max];                            //每个结点的势
int dis[Max];
int pre_node[Max], pre_edge[Max];    //前驱结点和对应边void add(int u, int v, int flow, int cost)
{edge[u].push_back({ v,(int)edge[v].size(),flow,cost });edge[v].push_back({ u,(int)edge[u].size() - 1,0,-cost });
}void min_cost_flow(int s, int t, int& min_cost, int& max_flow)
{fill(h + 1, h + 1 + n, 0);min_cost = max_flow = 0;int tot = inf;                            //源点流量无限while (tot > 0){priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q;memset(dis, inf, sizeof(dis));dis[s] = 0;q.push({ 0,s });while (!q.empty()){int u = q.top().second;int dist = q.top().first;q.pop();if (dis[u] < dist)continue;        //当前的距离不是最近距离for (int i = 0;i < edge[u].size(); i++){Edge &e = edge[u][i];if (edge[u][i].flow > 0 && dis[e.to] > dis[u] + e.cost + h[u] - h[e.to]){dis[e.to] = dis[u] +e.cost + h[u] - h[e.to];pre_node[e.to] = u;pre_edge[e.to] = i;q.push({ dis[e.to],e.to });}}}if (dis[t] == inf)break;            //无法增广了,就是找到答案了for (int i = 1;i <= n;i++) h[i] += dis[i];int flow = tot;                        //求这一增广路径的流量for (int i = t; i != s; i = pre_node[i])flow = min(flow, edge[pre_node[i]][pre_edge[i]].flow);for (int i = t; i != s; i = pre_node[i]){Edge& e = edge[pre_node[i]][pre_edge[i]];e.flow -= flow;edge[i][e.rev].flow += flow;}tot -= flow;max_flow += flow;min_cost += flow * h[t];}
}int main()
{
#ifdef LOCAL//freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);
#endifint s, t;while (scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){for (int i = 1, u, v, flow, cost;i <= m;i++){scanf("%d%d%d%d", &u, &v, &flow, &cost);add(u, v, flow, cost);}int min_cost, max_flow;min_cost_flow(s, t, min_cost, max_flow);printf("%d %d\n", max_flow, min_cost);}return 0;
}
无负环图中可用的算法-dijkstra(这里给出的是可以适用于有负环的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>
#define bug cout << "**********" << endl
#define show(x,y) cout<<"["<<x<<","<<y<<"] "
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const int Max = 1e5 + 10;struct Edge
{int to, next;int flow, cost;
}edge[Max << 1];int n, m, s, t;
int head[Max], tot;
int dis[Max];
int pre[Max];            //记录增广路径此点的前一天边
bool vis[Max];void init()
{memset(head, -1, sizeof(head));tot = 0;
}void add(int u, int v, int flow, int cost)
{edge[tot].to = v;edge[tot].flow = flow;edge[tot].cost = cost;edge[tot].next = head[u];head[u] = tot++;edge[tot].to = u;edge[tot].flow = 0;edge[tot].cost = -cost;edge[tot].next = head[v];head[v] = tot++;
}bool spfa(int s, int t)
{memset(dis, inf, sizeof(dis));memset(vis, 0, sizeof(vis));memset(pre, -1, sizeof(pre));queue<int>q;q.push(s);dis[s] = 0;vis[s] = true;while (!q.empty()){int u = q.front();q.pop();vis[u] = false;for (int i = head[u]; i != -1; i = edge[i].next){int v = edge[i].to;if (edge[i].flow > 0 && dis[v] > dis[u] + edge[i].cost){dis[v] = dis[u] + edge[i].cost;pre[v] = i;if (!vis[v]){vis[v] = true;q.push(v);}}}}return pre[t] != -1;
}void min_cost_max_flow(int s, int t, int& max_flow, int& min_cost)
{max_flow = 0;min_cost = 0;while (spfa(s, t)){int flow = inf;for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to])    //沿增广路回溯edge[i^1]即为其反边
        {flow = min(flow, edge[i].flow);}for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]){edge[i].flow -= flow;edge[i ^ 1].flow += flow;min_cost += flow * edge[i].cost;}max_flow += flow;}
}int main()
{
#ifdef LOCALfreopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#endifwhile (scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){init();for (int i = 1, u, v, flow, cost;i <= m;i++){scanf("%d%d%d%d", &u, &v, &flow, &cost);add(u, v, flow, cost);}int max_flow = 0, min_cost = 0;min_cost_max_flow(s, t, max_flow, min_cost);printf("%d %d\n", max_flow, min_cost);}return 0;
}
常用且比较高效的算法-spfa

 

转载于:https://www.cnblogs.com/winter-bamboo/p/11330082.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字经济的核心是对大数据_大数据崛起为数字世界的核心润滑剂

数字经济的核心是对大数据“Information is the oil of the 21st century, and analytics is the combustion engine”.“信息是21世纪的石油&#xff0c;分析是内燃机”。 — Peter Sondergaard, Senior Vice President of Gartner Research.— Gartner研究部高级副总裁Peter…

制作简单的WIFI干扰器

原教程链接:http://www.freebuf.com/geek/133161.htmlgithub 1.准备材料 制作需要的材料有 nodemcu开发版IIC通信 128*64 OLED液晶屏电线按钮开关万能板排针(自选)双面胶(自选)参考2.准备焊接 引脚焊接参考 oled按钮效果3.刷入固件 下载烧录工具:ESP8266Flasher.exe 下载固件:…

Snipaste截图

绘图绘色&#xff0c;描述加图片能更加说明问题的本质。今天推荐一款多功能的截图snipaste... 欣赏绘色 常见报错 解决方案&#xff1a; 下载相关的DLL即可解决&#xff0c; 请根据你操作系统的版本&#xff08;32位/64位&#xff09;&#xff0c;下载并安装相应的微软 Visual …

azure第一个月_MLOps:两个Azure管道的故事

azure第一个月Luuk van der Velden and Rik Jongerius卢克范德费尔登(Luuk van der Velden)和里克 琼格里乌斯( Rik Jongerius) 目标 (Goal) MLOps seeks to deliver fresh and reliable AI products through continuous integration, continuous training and continuous del…

VS2008 开发设计MOSS工作流 URN 注意了

最近学习MOSS 很苦恼&#xff0c;进度也很慢&#xff0c;最近在学习VS2008开发工作流&#xff0c;其中有结合INFOPATH 2007来做, 出现个BUG或者说是设置的问题,整整花了我一天工作时间&#xff0c;是这样的: 在部署的时候关于URN&#xff0c;大部分的教程都是这样的说的&#…

ArangoDB Foxx service 使用

备注&#xff1a;项目使用的是github https://github.com/arangodb-foxx/demo-hello-foxx1. git clonegit clone https://github.com/arangodb-foxx/demo-hello-foxx.git 2. 安装foxx servicefoxx-manager install demo-hello-foxx /demoapp 3. 效果自动生成的swagger 文档项目…

编译原理 数据流方程_数据科学中最可悲的方程式

编译原理 数据流方程重点 (Top highlight)Prepare a box of tissues! I’m about to drop a truth bomb about statistics and data science that’ll bring tears to your eyes.准备一盒纸巾&#xff01; 我将投放一本关于统计和数据科学的真相炸弹&#xff0c;这会让您眼泪汪…

iOS-FMDB

2019独角兽企业重金招聘Python工程师标准>>> #import <Foundation/Foundation.h> #import <FMDatabase.h> #import "MyModel.h"interface FMDBManager : NSObject {FMDatabase *_dataBase; }(instancetype)shareInstance;- (BOOL)insert:(MyM…

解决朋友圈压缩_朋友中最有趣的朋友[已解决]

解决朋友圈压缩We live in uncertain times.我们生活在不确定的时代。 We don’t know when we’re going back to school or the office. We don’t know when we’ll be able to sit inside at a restaurant. We don’t even know when we’ll be able to mosh at a Korn co…

MapServer应用开发平台示例

MapServer为当前开源WebGIS的应用代表&#xff0c;在西方社会应用面极为广泛&#xff0c;现介绍几个基于它的开源应用平台。 1.GeoMOOSE GeoMoose is a Web Client Javascript Framework for displaying distributed cartographic data. Among its many strengths, it can hand…

pymc3 贝叶斯线性回归_使用PyMC3进行贝叶斯媒体混合建模,带来乐趣和收益

pymc3 贝叶斯线性回归Michael Johns, Zhenyu Wang, Bruno Dupont, and Luca Fiaschi迈克尔约翰斯&#xff0c;王振宇&#xff0c;布鲁诺杜邦和卢卡菲亚斯基 “If you can’t measure it, you can’t manage it, or fix it”“如果无法衡量&#xff0c;就无法管理或修复它” –…

ols线性回归_普通最小二乘[OLS]方法使用于机器学习的简单线性回归变得容易

ols线性回归Hello Everyone!大家好&#xff01; I am super excited to be writing another article after a long time since my previous article was published.自从上一篇文章发表很长时间以来&#xff0c;我很高兴能写另一篇文章。 A Simple Linear Regression [SLR] is…

Amazon Personalize:帮助释放精益数字业务的高级推荐解决方案的功能

By Gerd Wittchen盖德维琴 推荐解决方案的动机 (Motivation for recommendation solutions) Rapid changes in customer behaviour requires businesses to adapt at an ever increasing pace. The recent changes to our work and personal life has forced entire nations t…

Linux 链接文件讲解

链接文件是Linux文件系统的一个优势。如需要在系统上维护同一文件的两份或者多份副本&#xff0c;除了保存多份单独的物理文件之外&#xff0c;可以采用保留一份物理文件副本和多个虚拟副本的方式&#xff0c;这种虚拟的副本就成为链接。链接是目录中指向文件真实位置的占位符。…

系统滚动条实现的NUD控件Unusable版

昨天研究了一下系统滚动条&#xff0c;准备使用它来实现一个NumericUpDown控件&#xff0c;因为它可以带来最正宗的微调按钮外观&#xff0c;并说了一下可以使用viewport里的onScroll事件来获取系统滚动条的上下点击动作。 同时昨天还说了onScroll事件的一个问题是&#xf…

[习题].FindControl()方法 与 PlaceHolder控件 #2(动态加入「子控件」的事件)

这是我的文章备份&#xff0c;有空请到我的网站走走&#xff0c; http://www.dotblogs.com.tw/mis2000lab/ 才能掌握我提供的第一手信息&#xff0c;谢谢您。 http://www.dotblogs.com.tw/mis2000lab/archive/2011/07/26/placeholder_findcontrol_eventhandler.aspx [习题].Fi…

西雅图治安_数据科学家对西雅图住宿业务的分析

西雅图治安介绍 (Introduction) Airbnb provides an online platform for hosts to accommodate guests with short-term lodging. Guests can search for lodging using filters such as lodging type, dates, location, and price, and can search for specific types of hom…

【贪心】买卖股票的最佳时机含手续费

/** 贪心&#xff1a;每次选取更低的价格买入&#xff0c;遇到高于买入的价格就出售(此时不一定是最大收益)。* 使用buy表示买入股票的价格和手续费的和。遍历数组&#xff0c;如果后面的股票价格加上手续费* 小于buy&#xff0c;说明有更低的买入价格更新buy。如…

排序算法Java代码实现(二)—— 冒泡排序

本篇内容&#xff1a; 冒泡排序冒泡排序 算法思想&#xff1a; 冒泡排序的原理是&#xff1a;从左到右&#xff0c;相邻元素进行比较。 每次比较一轮&#xff0c;就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 代码实现&#xff1a; /*** */ packag…

创意产品 分析_使用联合分析来发展创意

创意产品 分析Advertising finds itself in a tenacious spot these days serving two masters: creativity and data.如今&#xff0c;广告业处于一个顽强的位置&#xff0c;服务于两个大师&#xff1a;创造力和数据。 On the one hand, it values creativity; and it’s not…