最大流的学习代码,匹配问题的学习 使用c++

使用一个例子学习最大流和匹配问题,假如某地有n个出租车司机和m个正在打车的乘客,他们应该如何匹配;

#include <iostream>  // 用于输入输出
#include <vector>    // 用于动态数组
#include <queue>     // 用于广度优先搜索
#include <limits>    // 用于获取整型最大值
#include <algorithm> // 用于使用std::fill和std::min函数// 图类,用于表示网络流图
class Graph {
private:int V; // 顶点数std::vector<std::vector<int>> capacity; // 容量矩阵std::vector<std::vector<int>> adj; // 邻接表public:// 构造函数,初始化图Graph(int V) : V(V) {capacity.resize(V, std::vector<int>(V, 0)); // 初始化容量矩阵adj.resize(V); // 初始化邻接表}// 添加边到图中void addEdge(int u, int v, int cap) {adj[u].push_back(v); // 添加正向边adj[v].push_back(u); // 添加反向边(用于残余网络)capacity[u][v] = cap; // 设置正向边的容量}// 广度优先搜索,寻找增广路径int bfs(int s, int t, std::vector<int>& parent) {std::fill(parent.begin(), parent.end(), -1); // 初始化父节点数组parent[s] = -2; // 标记源点已访问std::queue<std::pair<int, int>> q; // 用于BFS的队列,存储节点和到达该节点的流量q.push({s, std::numeric_limits<int>::max()}); // 将源点入队while (!q.empty()) {int cur = q.front().first; // 当前节点int flow = q.front().second; // 到达当前节点的流量q.pop();for (int next : adj[cur]) { // 遍历当前节点的所有邻接节点if (parent[next] == -1 && capacity[cur][next]) { // 如果邻接节点未访问且有剩余容量parent[next] = cur; // 记录父节点int new_flow = std::min(flow, capacity[cur][next]); // 计算新的流量if (next == t) // 如果到达汇点return new_flow; // 返回找到的增广路径的流量q.push({next, new_flow}); // 将邻接节点入队}}}return 0; // 没有找到增广路径,返回0}// 计算最大流int maxFlow(int s, int t) {int flow = 0; // 总流量std::vector<int> parent(V); // 用于存储增广路径int new_flow;// 当还能找到增广路径时while (new_flow = bfs(s, t, parent)) {flow += new_flow; // 增加总流量int cur = t;while (cur != s) { // 更新残余网络int prev = parent[cur];capacity[prev][cur] -= new_flow; // 正向边减少容量capacity[cur][prev] += new_flow; // 反向边增加容量cur = prev;}}return flow; // 返回最大流}
};// 出租车匹配器类
class TaxiMatcher {
private:int n, m; // n个司机,m个乘客Graph graph; // 用于表示匹配问题的图public:// 构造函数,初始化匹配器TaxiMatcher(int n, int m) : n(n), m(m), graph(n + m + 2) {int source = 0; // 源点int sink = n + m + 1; // 汇点// 添加从源点到所有司机的边,容量为1for (int i = 1; i <= n; i++) {graph.addEdge(source, i, 1);}// 添加从所有乘客到汇点的边,容量为1for (int i = 1; i <= m; i++) {graph.addEdge(n + i, sink, 1);}}// 添加可能的匹配void addPossibleMatch(int driver, int passenger) {graph.addEdge(driver, n + passenger, 1); // 添加从司机到乘客的边,容量为1}// 寻找最大匹配数int findMaxMatches() {return graph.maxFlow(0, n + m + 1); // 计算从源点到汇点的最大流}
};int main() {int n, m;std::cout << "请输入出租车司机数量: ";std::cin >> n;std::cout << "请输入旅客数量: ";std::cin >> m;TaxiMatcher matcher(n, m); // 创建匹配器std::cout << "请输入可能的匹配(司机编号 旅客编号),输入 -1 -1 结束:\n";while (true) {int driver, passenger;std::cin >> driver >> passenger;if (driver == -1 && passenger == -1) break;matcher.addPossibleMatch(driver, passenger); // 添加可能的匹配}int maxMatches = matcher.findMaxMatches(); // 计算最大匹配数std::cout << "最大匹配数量: " << maxMatches << std::endl;return 0;
}

算法思想解释:

  1. 这段代码实现了最大流算法(Ford-Fulkerson方法的Edmonds-Karp实现)来解决二分图最大匹配问题。
  2. 二分图最大匹配问题被转化为最大流问题:
    • 创建一个源点(source)和一个汇点(sink)。
    • 从源点到每个司机添加一条容量为1的边。
    • 从每个乘客到汇点添加一条容量为1的边。
    • 对于每个可能的匹配(司机到乘客),添加一条容量为1的边。
  3. 使用广度优先搜索(BFS)来寻找增广路径。这保证了算法的时间复杂度为O(VE^2),其中V是顶点数,E是边数。
  4. 通过反复寻找增广路径并更新残余网络,直到无法找到更多的增广路径,从而计算出最大流。
  5. 最终的最大流值就是最大匹配数,因为每条从源点到汇点的路径代表一个匹配。

这种方法巧妙地将二分图最大匹配问题转化为了网络流问题,利用了最大流-最小割定理来解决。它不仅可以处理完美匹配,也可以处理不完全匹配的情况,是一个非常通用和强大的方法。

备注:

这段代码使用的是 Edmonds-Karp 算法,它是 Ford-Fulkerson 方法的一种实现。我会详细解释这个算法,并给出一个例子。

最大流算法解释:

  1. Ford-Fulkerson 方法: 这是一种用于寻找网络中最大流的迭代方法。其基本思想是:
    • 只要存在一条从源点到汇点的增广路径(即还有剩余容量的路径),就沿着这条路径增加流量。
    • 重复这个过程,直到找不到增广路径为止。
  2. Edmonds-Karp 算法: 这是 Ford-Fulkerson 方法的一个具体实现,其特点是使用广度优先搜索(BFS)来寻找增广路径。这保证了算法的时间复杂度为 O(VE^2),其中 V 是顶点数,E 是边数。

算法步骤:

  1. 初始化流为 0。
  2. 使用 BFS 寻找一条从源点到汇点的增广路径。
  3. 确定这条路径上的最小剩余容量。
  4. 将这个最小容量加到总流量上。
  5. 更新残余网络:沿着增广路径,正向边的容量减少,反向边的容量增加。
  6. 重复步骤 2-5,直到找不到增广路径。

下面详细解释一下类:

1.graph类

class Graph {
private:int V; // 顶点数std::vector<std::vector<int>> capacity; // 容量矩阵std::vector<std::vector<int>> adj; // 邻接表public:Graph(int V); // 构造函数void addEdge(int u, int v, int cap); // 添加边int bfs(int s, int t, std::vector<int>& parent); // 广度优先搜索int maxFlow(int s, int t); // 计算最大流
};

解释:

  • V 存储图中的顶点数。
  • capacity 是一个二维vector,表示边的容量。capacity[u][v] 表示从顶点u到顶点v的边的容量。
  • adj 是邻接表,用于存储图的结构。
  • addEdge 方法用于添加边到图中。
  • bfs 方法实现了广度优先搜索,用于寻找增广路径。
  • maxFlow 方法实现了 Edmonds-Karp 算法,计算从源点到汇点的最大流。

2.TaxiMatcher 类

TaxiMatcher 类是一个高层抽象,它使用 Graph 类来解决出租车匹配问题。

class TaxiMatcher {
private:int n, m; // n个司机,m个乘客Graph graph; // 用于表示匹配问题的图public:TaxiMatcher(int n, int m); // 构造函数void addPossibleMatch(int driver, int passenger); // 添加可能的匹配int findMaxMatches(); // 寻找最大匹配数
};

解释:

1.n和m分别表示司机司机数量和乘客数量。

2.graph是一个graph对象,用于表示整个匹配问题。

3.构造函数创建了一个n+m+2个顶点的图(n个司机,m个乘客,1个源点,一个汇点)

4.addossibleMatch方法用于添加一个可能的司机-乘客匹配。

5.findMaxMatches 方法通过调用graph的maxFlow方法来计算最大匹配数。

这两个类的协作方式:

1.Taximatch在构建时构建一个特殊的结构graph:

  • 顶点 0 作为源点
  • 顶点 1 到 n 表示司机
  • 顶点 n+1 到 n+m 表示乘客
  • 顶点 n+m+1 作为汇点
  • 从源点到每个司机添加容量为1的边
  • 从每个乘客到汇点添加容量为1的边

2.当调用addPossibleMatch时,TaxiMatcher调用Graph中的maxFlow方法,计算从源点到汇点的最大流,这个最大流值就是最大匹配数。

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

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

相关文章

linux基于wifi,Xshell的远程连接

最近有个比赛&#xff0c;要使用ros小车但是系统是ubuntu20.04无桌面系统刚开始接触linux的我啥都不会&#xff0c;就一个简单的连接wifi都搞了3天才搞通。再此进行一个总结。参考博客原文链接&#xff1a;https://blog.csdn.net/qq_51491920/article/details/126221940 一、什…

短说V4.1.5及PC端V3.1.4正式版发布公告

Hi 大家好&#xff0c; 我是给你们带来惊喜的运营小番茄。 本期更新为短说 4.1.5和PC端3.1.4的正式版。 本次修复上个版本中的问题和功能优化&#xff0c;以及新增了如下功能&#xff1a; PC端支持发布秀米帖&#xff0c;可支持部分秀米格式&#xff1b;后台管理类消息新增…

C#中的容器

1、数组 数组是存储相同类型元素的固定大小的顺序集合 声明数组时&#xff0c;必须指定数组的大小 2.数组的插入和删除数据比较麻烦&#xff0c;但是查询比较快 2、动态数组&#xff08;ArrayList&#xff09; 动态数组&#xff1a;可自动调节数组的大小 可以存储任意类型数…

【秋招刷题打卡】Day03-二分系列之-二分答案

Day03-二分系列之-二分答案 给大家推荐一下咱们的 陪伴打卡小屋 知识星球啦&#xff0c;详细介绍 >笔试刷题陪伴小屋-打卡赢价值丰厚奖励 < ⏰小屋将在每日上午发放打卡题目&#xff0c;包括&#xff1a; 一道该算法的模版题 (主要以力扣&#xff0c;牛客&#xff0c;…

43 mysql insert select 的实现

前言 我们这里 来探讨一下 insert into $fields select $fields from $table; 的相关实现, 然后 大致来看一下 为什么 他能这么快 按照 我的思考, 应该里里面有 批量插入才对, 但是 调试结果 发现令我有一些意外 呵呵 果然 只有调试才是唯一的真理 测试数据表如下 CREATE…

数字社交的领航者:解析Facebook的引领作用

在当今数字化社会中&#xff0c;社交网络已经成为了人们日常生活不可或缺的一部分。而在众多社交平台中&#xff0c;Facebook凭借其巨大的用户基础和创新的技术应用&#xff0c;被公认为数字社交领域的领航者之一。本文将深入解析Facebook在数字社交中的引领作用&#xff0c;探…

一起学Hugging Face Transformers(3) - Hugging Face 加载模型和Tokenizer

文章目录 前言一、加载预训练模型和Tokenizer1. 安装Transformers库2. 加载预训练模型和Tokenizer 二、使用AutoModel和AutoTokenizer1. 使用AutoModel和AutoTokenizer2. 原理1&#xff09; 配置文件 (config.json)2&#xff09; 使用AutoModel3&#xff09; 使用AutoTokenizer…

Vue.js 的 provide 选项来向子组件提供数据

父组件 // Parent.vue <template><div><p>Parent Component</p><p>Count: {{ count }}</p><ChildComponent /></div> </template><script> import ChildComponent from ./ChildComponent.vue;export default {co…

这三款工具很好用,赶快试试

FileZilla FileZilla是一款免费开源的FTP软件&#xff0c;分为客户端版本和服务器版本&#xff0c;具备所有的FTP软件功能。它是一个快速、可信赖的FTP客户端以及服务器端开放源代码程序&#xff0c;具有多种特色和直觉的界面。FileZilla客户端版是一个方便高效的FTP客户端工具…

第一后裔The First Descendant开服时间、配置要求一览

第一后裔是一款采用虚幻5引擎打造的第三人称合作射击动作RPG&#xff0c;玩家将化身为一名继承者&#xff0c;通过各种任务和故事不断成长&#xff0c;为守护人类与对抗侵略者战斗。该作即将上线&#xff0c;为了不让玩家们错过这款精彩的游戏&#xff0c;本文整理了第一后裔上…

卫生间和厨房墙面基层起沙怎么办?

最近有几个工地遇到了一个共同问题&#xff0c;卫生间和厨房墙面起沙。      如果有正在装修的业主&#xff0c;可以看一下你家墙面是否也存在这样的问题。      最简单的检测方法&#xff0c;在工地上找一个坚硬的东西在墙上划&#xff0c;如果墙上的沙粒子一直哗哗的…

测评:【AI办公】版本更迭与AI加持下的最新ONLYOFFICE桌面编辑器8.1

你是否还在为没有一款合适的在线桌面编辑器而苦恼&#xff1f;你是否还在因为办公软件的选择过少而只能使用WPS或者office&#xff1f;随着办公需求的不断变化和发展&#xff0c;办公软件也在不断更新和改进。ONLYOFFICE 作为一款全功能办公软件&#xff0c;一直致力于为用户提…

2024年全国青少年信息素养大赛图形化编程复赛样题_6547网

第 1 题 问答题 【编程实现】 按空格键随机切换背景&#xff0c;让背景对应的角色造型显示在舞台上。 【具体要求】 对角色编程&#xff0c;当按下空格键时&#xff0c;背景随机切换&#xff1b; 角色切换成对应的造型显示在舞台上&#xff1b; 角色说“我是”和它的造…

帮助你简易起步一个BLOG(博客搭建)项目

Blog项目 后端项目结构1. 项目初始化2. 详细步骤3.postman测试 前端1. 项目初始化2. 详细步骤 本章节是为了帮助你起步一个完整的前后端分离项目。 前端技术栈&#xff1a; react、vite、mantine、tailwind CSS、zustand、rxjs、threejs 后端技术栈&#xff1a;nodemon、nodej…

计算机网络 5.2网卡

第二节 网卡 一、认识网卡 1.工作方式&#xff1a;中断请求。 2.应用场合&#xff1a;接入局域网。 3.功能&#xff1a; ①实现局域网中传输介质的物理连接和电气连接。 ②拥有一个全球唯一的网卡地址&#xff08;长度为48位的二进制数&#xff09;。 ③执行网络控制命令…

深入探索Batch脚本:实现延迟执行的高级技巧

在Windows操作系统中&#xff0c;批处理文件&#xff08;Batch文件&#xff09;是一种自动化脚本&#xff0c;允许用户执行一系列命令和操作。在某些情况下&#xff0c;我们可能需要在批处理文件中创建延迟执行的命令&#xff0c;即让某些操作在预定的时间后执行。本文将详细介…

C语言 什么是算术运算?什么是关系运算?什么是逻辑运算?如何表示“真”和“假”?系统如何判断一个量的“真”和“假”?

算术运算&#xff1a;指的是基本的数学运算&#xff0c;包括加法&#xff08;&#xff09;、减法&#xff08;-&#xff09;、乘法&#xff08;*&#xff09;、除法&#xff08;/&#xff09;、取模&#xff08;%&#xff09;。 关系运算&#xff1a;用于比较两个值之间的大小…

Transformer教程之神经网络和深度学习基础

在当今的人工智能领域&#xff0c;Transformer已经成为了一个热门的词汇。它不仅在自然语言处理&#xff08;NLP&#xff09;领域取得了巨大的成功&#xff0c;还在计算机视觉等其他领域展现出了强大的潜力。然而&#xff0c;要真正理解Transformer&#xff0c;我们首先需要扎实…

gdb用法

创建文件 // main.cpp文件 // 稳态误差 void pid_test_wentaiwucha() {float p 1.5;int t 1; // t 1s;int target 5; // 5m/sfloat output 0;float radis 3; // 稳态误差std::cout << "output: " << std::endl;fo…

析构函数:C++中的清洁工

目录 前言 什么是析构函数&#xff1f; 析构函数的特点&#xff1a; 何时需要显式定义析构函数&#xff1f; 显式定义析构函数的必要性 编写析构函数的注意事项 结论 前言 在C编程中&#xff0c;析构函数是一个重要的概念&#xff0c;它负责在对象生命周期结束时释放资…