最大流解决二分图匹配问题

文章目录

    • 零、前言
    • 一、二分图匹配转化为网络流模型
      • 1.1建模步骤
      • 1.2整数值最大流和二分图匹配的关系
      • 1.3代码实现
    • 二、OJ练习
      • P2756 飞行员配对方案问题
      • P3254 圆桌问题


零、前言

阅读本文前,需具备以下知识:

二分图及染色法判定-CSDN博客

二分图最大匹配——匈牙利算法详解-CSDN博客

最大流—EK算法,流网络,残留网络,定理证明,详细代码-CSDN博客

最大流-Dinic算法,原理详解,四大优化,详细代码-CSDN博客


一、二分图匹配转化为网络流模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.1建模步骤

  1. 二分图G中创建虚拟源点s,虚拟汇点t,s和左部点边,t和右部点连边
  2. 得到新图G‘,新图G’中所有边容量设为1
  3. 在G‘中寻找整数值最大流f,原二分图G中所有有流量的边即为最大匹配
    在这里插入图片描述

1.2整数值最大流和二分图匹配的关系

对于流网络中的最大流,不一定为整数,也可以是浮点数,所以我们有两个问题:

整数值可行流是否是二分图中一个匹配,二分图中的一个匹配是否对应一个整数值可行流?

对于可行流而言,由于所有边的容量上限都为1,所以每个左部点最多流经1点流量,也最多将这1点流量流向一个右部点,即每个左部点最多和一个右部点建立流量,换句话说,任取两条有流量的边,必然没有公共点,所以可行流是一个匹配。

那么对于一个匹配而言,我们匹配边赋予1点流量,再建立虚拟源点和虚拟汇点,我们发现新图除源汇点外满足容量守恒,斜对称和容量限制,所以是一个可行流。

于是建立了整数可行流和二分图最大匹配之间的双射关系。

为什么一定能找到一个整数值最大流

由于建立的流网络中只用到了整数值,我们求解最大流的算法也都只用到了整数值,所以最大流一定是整数流

1.3代码实现

以P3386 【模板】二分图最大匹配 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)为模板
时间复杂度:(On m^0.5)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1010, M = (50000 + N) << 1, inf = 1e9;
struct edge
{int v, c, nxt;
} edges[M];
int n, m, e, s, t, head[N], d[N], cur[N], idx = 0;
inline void addedge(int u, int v, int c)
{edges[idx] = {v, c, head[u]}, head[u] = idx++;
}
inline void add(int u, int v, int c)
{addedge(u, v, c), addedge(v, u, 0);
}
int dfs(int u, int limit)
{if (u == t)return limit;int res = 0;for (int i = cur[u]; ~i && limit; i = edges[i].nxt){cur[u] = i;int v = edges[i].v;if (d[v] == d[u] + 1 && edges[i].c){int incf = dfs(v, min(limit, edges[i].c));if (!incf)d[v] = 0;limit -= incf, res += incf, edges[i].c -= incf, edges[i ^ 1].c += incf;}}return res;
}
bool bfs()
{memset(d, 0, sizeof(d));queue<int> q;q.emplace(s), d[s] = 1;while (q.size()){int u = q.front();q.pop();for (int i = head[u]; ~i; i = edges[i].nxt){int v = edges[i].v;if (!d[v] && edges[i].c){d[v] = d[u] + 1, q.emplace(v);if (v == t)return true;}}}return false;
}
int dinic()
{int res = 0;while (bfs())memcpy(cur, head, sizeof(head)), res += dfs(s, inf);return res;
}
int main()
{ios::sync_with_stdio(false), cin.tie(0), cout.tie(0), memset(head, -1, sizeof(head));// freopen("in.txt", "r", stdin);int a, b, c;cin >> n >> m >> e, s = 0, t = n + m + 1;for (int i = 0; i < e; i++)cin >> a >> b, add(a, b + n, 1);for (int i = 1; i <= n; i++)add(s, i, 1);for (int i = 1; i <= m; i++)add(i + n, t, 1);cout << dinic();return 0;
}

二、OJ练习

P2756 飞行员配对方案问题

P2756 飞行员配对方案问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

很显然是一个二分图最大匹配问题,建图跑板子即可,然后题目还要求我们输出匹配边的两个节点,我们遍历匹配边,反向边和正向边的邻接点即为两个匹配点

F1 Dinic

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
#include <unordered_set>
#include <map>
#include <bitset>
using namespace std;
#define sc scanf
#define int long long
#define N 105
#define M 10010
const int MOD = 10000007;
const int inf = 0x3f3f3f3f3f3f3f3f;int n, m, s, t, idx = 0;
int d[N], cur[N], head[N]; // 深度,当前边,前向星头
struct edge
{int v, c, nxt;
} edges[M];inline void addedge(int u, int v, int c)
{edges[idx] = {v, c, head[u]};head[u] = idx++;
}bool bfs() // 多路增广
{memset(d, 0, sizeof(d));queue<int> q;q.emplace(s), d[s] = 1;while (q.size()){int u = q.front();q.pop();for (int i = head[u]; ~i; i = edges[i].nxt){int v = edges[i].v;if (!d[v] && edges[i].c){d[v] = d[u] + 1;q.emplace(v);if (v == t)return true;}}}return false;
}int dfs(int u, int limit)
{if (u == t)return limit;int ret = 0;for (int i = cur[u]; ~i && limit > 0; i = edges[i].nxt){cur[u] = i;int v = edges[i].v;if (d[v] == d[u] + 1 && edges[i].c){int incf = dfs(v, min(limit, edges[i].c));if (!incf)d[v] = 0;edges[i].c -= incf, edges[i ^ 1].c += incf, ret += incf, limit -= incf;}}return ret;
}int dinic()
{int ret = 0;while (bfs())memcpy(cur, head, sizeof(head)), ret += dfs(s, inf);return ret;
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);// freopen("in.txt", "r", stdin);memset(head, -1, sizeof(head));cin >> m >> n;int a, b;s = 0, t = n + 1;memset(head, -1, sizeof(head));for (int i = 1; i <= m; i++)addedge(s, i, 1), addedge(i, s, 0);for (int i = m + 1; i <= n; i++)addedge(i, t, 1), addedge(t, i, 0);while (1){cin >> a >> b;if (a == -1 && b == -1)break;addedge(a, b, 1), addedge(b, a, 0);}cout << dinic() << '\n';for (int i = 0; i < idx; i += 2)if (edges[i].v > m && edges[i].v <= n && !edges[i].c)cout << edges[i ^ 1].v << " " << edges[i].v << '\n';
}

F2 匈牙利求最大匹配

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
#include <unordered_set>
#include <map>
#include <bitset>
using namespace std;
#define sc scanf
#define int long long
#define N 105
#define M 10010
const int MOD = 10000007;
const int inf = 0x3f3f3f3f3f3f3f3f;int n, m, idx, ans = 0;
int match[N]{0}, head[N];
bool vis[N];
struct edge
{int v, nxt;
} edges[M << 1];inline void addedge(int u, int v)
{edges[idx] = {v, head[u]};head[u] = idx++;
}bool dfs(int u)
{for (int i = head[u]; ~i; i = edges[i].nxt){int v = edges[i].v;if (vis[v])continue;vis[v] = 1;if (!match[v] || dfs(match[v])){match[v] = u;return true;}}return false;
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);// freopen("in.txt", "r", stdin);memset(head, -1, sizeof(head));cin >> m >> n;int a, b;memset(head, -1, sizeof(head));while (1){cin >> a >> b;if (a == -1 && b == -1)break;addedge(a, b);}for (int i = 1; i <= m; i++)memset(vis, 0, sizeof(vis)), ans += dfs(i);cout << ans << '\n';for (int i = m + 1; i <= n; i++)if (match[i])cout << match[i] << " " << i << '\n';
}

P3254 圆桌问题

P3254 圆桌问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

仍然是二分图最大匹配,不过这里是多重匹配,处理多重匹配我们的策略是拆点,当然可以用匈牙利来做,这里我们直接用Dinic,体会一下如何将问题抽象为网络流问题。

这里建图相较于匈牙利解法就很爽了,每个左部点右部点最大连边数就是它们跟源点汇点边的容量,然后左右部点之间互相连容量为1的边,然后跑板子即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;const int N = 430, M = (150 * 270 + N) * 2;
const int inf = 1e9;
int n, m, s, t, idx = 0, cur[N], head[N], d[N];
struct edge
{int v, c, nxt;
} edges[M];
inline void addedge(int u, int v, int c)
{edges[idx] = {v, c, head[u]};head[u] = idx++;
}
inline void add(int u, int v, int c)
{addedge(u, v, c), addedge(v, u, 0);
}
bool bfs()
{memset(d, 0, sizeof(d));queue<int> q;q.emplace(s), d[s] = 1;while (q.size()){int u = q.front();q.pop();for (int i = head[u]; ~i; i = edges[i].nxt){int v = edges[i].v;if (!d[v] && edges[i].c){d[v] = d[u] + 1;q.emplace(v);if (v == t)return true;}}}return false;
}
int dfs(int u, int limit)
{if (u == t)return limit;int ret = 0;for (int i = cur[u]; ~i && limit; i = edges[i].nxt){cur[u] = i;int v = edges[i].v;if (d[v] == d[u] + 1 && edges[i].c){int incf = dfs(v, min(limit, edges[i].c));if (!incf)d[v] = 0;ret += incf, limit -= incf, edges[i].c -= incf, edges[i ^ 1].c += incf;}}return ret;
}
int dinic()
{int ret = 0;while (bfs())memcpy(cur, head, sizeof(head)), ret += dfs(s, inf);return ret;
}
int main()
{ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);freopen("in.txt", "r", stdin);cin >> m >> n;s = 0, t = m + n + 1;memset(head, -1, sizeof(head));int tot = 0;for (int i = 1; i <= m; i++){int r;cin >> r, add(s, i, r), tot += r;}for (int i = 1; i <= n; i++){int c;cin >> c, add(m + i, t, c);}for (int i = 1; i <= m; i++)for (int j = 1; j <= n; j++)add(i, m + j, 1);if (dinic() != tot){cout << '0';}else{cout << '1' << '\n';for (int i = 1; i <= m; i++){for (int j = head[i]; ~j; j = edges[j].nxt)if (edges[j].v > m && edges[j].v <= m + n && !edges[j].c)cout << edges[j].v - m << ' ';cout << '\n';}}
}

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

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

相关文章

PHP报错信息

PHP 推荐链接Thinkphp报错&#xff1a;SplFileInfo::getSize(): stat failed for 推荐链接 链接目录 Thinkphp报错&#xff1a;SplFileInfo::getSize(): stat failed for 先执行 move() 移动文件后&#xff0c;导致文件移除之后又执行了&#xff1a;$size $file->getSize…

【JavaSE】P114~P147 ArrayList集合,Scanner类,Random,字符串及相关常用方法,静态static

目录 1 ArrayList 集合装箱&#xff0c;拆箱及包装类 2 API 概述和使用Scanner类匿名对象Random生成随机数 3 字符串字符串的31种构造方法字符串的常量池equals和 字符串的获取相关方法字符串的截取方法字符串的转换相关方法字符串的分割方法 4 静态static关键字静态static的内…

对话泛能网程路:能源产业互联网,行至中程

泛能网的能源产业互联网的标杆价值还不仅于此。其在产业互联之外&#xff0c;也更大的特殊性在于其也更在成为整个碳市场的“辅助运营商”&#xff0c;包括电力、碳等一系列被泛能网帮助企业改造和沉淀的要素资产&#xff0c;都在构成着碳交易市场的未来底层。 这恰是产业互联…

PCL-IO输入输入模块

IO输入输入模块 一、概述二、点云数据格式1. PCD 格式2. PLY 格式3. OBJ 格式4. STL 格式5. OFF 格式 三、读取3D文件1. API 总览2. 示例 四、保存3D文件1. API 总览2. 示例 一、概述 PCL 库提供了一个模块用来对3D数据进行读写操作&#xff0c;这个库提供了一个模块&#xff…

2007-2022年全国货币供应量M2、失业率、CPI、第三方互联网支付、出口、人口等宏观经济指标数据(年度、季度)

2007-2022年全国货币供应量M2、失业率、CPI、第三方互联网支付、出口、人口等宏观经济指标数据&#xff08;年度、季度&#xff09; 1、时间&#xff1a;2007-2022年&#xff08;季度、年度&#xff09; 2、指标&#xff1a; 季度指标&#xff1a;时间、GDP不变价累计值(亿元…

python ffmpeg将mp4文件实时转码为ts,并指定pid等信息,输出到udp

要将MP4文件实时转码为TS格式&#xff0c;并将PID等信息指定为UDP输出&#xff0c;可以使用subprocess模块和ffmpeg命令行工具来实现。以下是一个示例代码&#xff0c;用于实时转码并将输出发送到UDP服务器&#xff1a; import subprocess import timeinput_file "input.…

性能优化-高通的Hexagon DSP和NPU

原文来自【 Qualcomm’s Hexagon DSP, and now, NPU 】 本文主要介绍Qualcomm Hexagon DSP和NPU&#xff0c;这些为处理简单大量运算而设计的硬件。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xf…

多臂老虎机理论系列

[多臂老虎机理论](Lower bounds for non-adaptive exploration) 多臂老虎机之Lower bounds 定理 2.12&#xff1a; 定理的意义在于&#xff0c;对于任何不调整探索策略的算法&#xff0c;存在至少一个问题实例&#xff0c;使得随着时间的推移&#xff0c;该算法的预期遗憾将…

Linux常见的管理命令

1. whoami 作用&#xff1a; 显示出当前有效的用户名称&#xff0c;Linux是多用户多任务 语法&#xff1a;whoami(选项) 选项&#xff1a; --help&#xff1a;在线帮助 --version&#xff1a;显示版本信息和退出 场景使用&#xff1a; 1. 当用户想要查看当前登录系统的用户…

14.case条件测试语句(5)

case语句是在多个范围内匹 配数据&#xff0c;若匹配成功则执行相关命令并结束整个条件测试&#xff1b;如果数据不在所列出的范围内&#xff0c; 则会去执行星号&#xff08;*&#xff09;中所定义的默认命令&#xff08;C语言中的default语句&#xff09; 提示用户输入一个字…

爬取樱花动漫名侦探柯南最新剧场版ts格式

import os import requests import zipfile from tqdm import tqdm import tkinter as tkfilename 名侦探柯南\\ if not os.path.exists(filename):os.mkdir(filename) # https://vip.ffzy-online6.com/20231129/22304_740e70d0/2000k/hls/cedd2dc1ecb000001.ts # https://vip…

硬件基础:存储器

之前对存储器做过简单的汇总&#xff0c;参考这篇文章&#xff1a; 计算机/微机存储技术_路溪非溪的博客-CSDN博客 这次&#xff0c;我们从数字集成电路的角度再次补充学习一下存储器的知识。 定义和分类 从这里面我们能知道一些关键词。 存储介质主要是半导体器件和磁性材料。…

Java技术栈高级攻略之专栏简介

Java是一种广泛应用于企业级应用开发的编程语言&#xff0c;具备强大的生态系统和丰富的工具支持。想要在Java技术栈中取得更高的成就&#xff0c;你需要不断深入学习和实践。本专栏为你提供很多高级攻略&#xff0c;帮助你更好地掌握Java技术栈。 一、多线程编程 多线程编程是…

亿发中小型企业erp软件智能化赋能,专业助力广东制造行业生产流程管理

在当前经济全球化的环境下&#xff0c;广东省的中小型制造业企业正面临多方面的严峻挑战。包括产品质量的维护、分销渠道的稳定、生产成本降低以及减轻生产过程中的资源消耗等难题。目前&#xff0c;随着信息技术的迅速发展&#xff0c;一些先进的IT工具&#xff0c;比如企业资…

【owt】webrtc的随机数生成

SSRC 是32位的随机值 在 WebRTC 中,确保 RTP(Real-time Transport Protocol)的 SSRC(Synchronization Source Identifier)不冲突是非常重要的。SSRC 是一个 32 位的标识符,用于标识 RTP 流中的同步源。理论上,由于 SSRC 是随机生成的,存在一定的冲突概率。但是,由于 3…

地址解析协议

地址解析协议ARP(Address Resolution Protocol)是用来将IP地址解析为MAC地址的协议 ARP数据包格式(直接封装在ethernet_II上面) 1.Hardware Type:表示硬件地址类型,一般为以太网; 2.Protocol Type:表示三层协议地址类型,一般为IP; 3.Hardware Length和Protocol Leng…

STM32实现软件IIC协议操作OLED显示屏(1)

时间记录&#xff1a;2024/1/25 一、IIC协议介绍 &#xff08;1&#xff09;协议介绍 IIC&#xff08;又称I2C&#xff0c;Inter-Integrated Circuit&#xff09;&#xff0c;即集成电路总线&#xff0c;是一种两线式串行总线&#xff0c;由PHILIPS公司开发&#xff0c;用…

OSS上传下载乱码问题

配置headers&#xff1a; "Content-Disposition": attachment; filename*UTF-8${encodeURIComponent(file.file.name)},

chatGPT辅助写硕士毕业论文

一、写作顺序 1.标题、研究问题、研究方法 2.文献综述&#xff08;占比1/5-1/6&#xff09; 3.论证章节 4.结论、不足、启示 5.处理图表、参考文献的格式 6.绪论或引言 7.摘要、关键词 8.查重、装订 http://【硕士毕业论文写不下去&#xff0c;多亏听了张博士的论文写…

【GitHub项目推荐--开源小游戏】【转载】

01 回合制生存游戏 Cataclysm-DDA 是一款回合制生存游戏&#xff0c;背景设置在后世界末日的世界中。虽然有些人将其描述为“僵尸游戏”&#xff0c;但《大灾变》远不止这些。努力在一个严酷、持久、程序生成的世界中生存。 为食物、设备寻找一个死去的文明的残余物。或者&am…