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

文章目录

    • 零、前言
    • 一、二分图匹配转化为网络流模型
      • 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,一经查实,立即删除!

相关文章

【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不变价累计值(亿元…

性能优化-高通的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;我们从数字集成电路的角度再次补充学习一下存储器的知识。 定义和分类 从这里面我们能知道一些关键词。 存储介质主要是半导体器件和磁性材料。…

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

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

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)},

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

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

ThinkPHP+uni-app框架熊猫电竞赏金电竞系统源码PHP含APP+H5

熊猫电竞赏金电竞系统源码&#xff0c;包含APP、H5和搭建视频教程&#xff0c;支持运营级搭建&#xff0c;这套源码是基于ThinkPHPUniaapp框架开发的。 赏金电竞系统源码 APPh5搭建视频 可搭建&#xff01;运营级&#xff01; 赏金赛源码&#xff0c;用户通过平台打比赛&#x…

小白水平理解面试经典题目LeetCode 594 Longest Harmonious Subsequence(最大和谐字符串)

594 最大和谐字符串 这道题属于字符串类型题目&#xff0c;解决的办法还是有很多的&#xff0c;暴力算法&#xff0c;二分法&#xff0c;双指针等等。 题目描述 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 现在&#xff0c;给你一个整数数组 nums …

用vue实现微信小程序的点餐首页-纯前端效果

一、效果图 图片来源于网络 二、代码 <template><view class"container"><view class"top"><image src"../../static/img/home.png" class"home"></image></view><view class"content&…

安装好IntelliJ IDEA点击无反应,如何解决配置文件不一致导致的启动问题

在我们的开发生涯中&#xff0c;遇到IDE工具出现问题是在所难免的。最令人头疼的莫过于&#xff0c;你的IDEA(IntelliJ IDEA)无法启动&#xff0c;而且没有任何错误提示。这篇文章将详细讲解如何解决IntelliJ IDEA 2023.3.3版本启动失败的问题&#xff0c;这个问题可能也适用于…

Linux的文件系统、软硬链接、动静态库

前要&#xff1a;本次我想给您带来关于 IO 和文件的知识&#xff0c;而文件在本系列中分为内存上的文件和磁盘上的文件。 1.文件概念 1.1.文件读写 在谈及系统接口之前&#xff0c;我们先来从 C 语言的角度来谈及一些前要知识&#xff0c;以辅助我们后续来理解系统 IO。 我们…

LeetCode.2859. 计算 K 置位下标对应元素的和

题目 题目链接 分析 这道题的题意很明确。就是求每一个下标的二进制中1的个数为k的下标所对应的元素值之和。 Java 中有 库函数 Integer.bitCount(num)&#xff0c;这个函数的返回值就是 num 中 1 的个数。 代码 class Solution {public int sumIndicesWithKSetBits(List…