匈牙利算法【匹配问题】
匈牙利算法:
匈牙利算法几乎是二分图匹配的核心算法,除了二分图多重匹配外均可使用
匈牙利算法实际上就是一种网络流的思想,其核心就是寻找增广路。具体操作就是嗯。。拉郎配
匈牙利算法适用的场合:
- 二分图匹配
- 链上的匹配问题
- 一般图匹配等其他点的个数较小的匹配问题
举个以前写的博客的例子
通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,在你的手上有N个剩男,M个剩女,每个人都可能对多名异性有好感(-_-||暂时不考虑特殊的性取向),如果一对男女互有好感,那么你就可以把这一对撮合在一起,现在让我们无视掉所有的单相思(好忧伤的感觉),你拥有的大概就是下面这样一张关系图,每一条连线都表示互有好感。
本着救人一命,胜造七级浮屠的原则,你想要尽可能地撮合更多的情侣,匈牙利算法的工作模式会教你这样做:
===============================================================================
一: 先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,got it,连上一条蓝线
===============================================================================
二:接着给2号男生找妹子,发现第一个和他相连的2号女生名花无主,got it
===============================================================================
三:接下来是3号男生,很遗憾1号女生已经有主了,怎么办呢?
我们试着给之前1号女生匹配的男生(也就是1号男生)另外分配一个妹子。
(黄色表示这条边被临时拆掉)
与1号男生相连的第二个女生是2号女生,但是2号女生也有主了,怎么办呢?我们再试着给2号女生的原配重新找个妹子(注意这个步骤和上面是一样的,这是一个递归的过程)
此时发现2号男生还能找到3号女生,那么之前的问题迎刃而解了,回溯回去
2号男生可以找3号妹子~~~ 1号男生可以找2号妹子了~~~ 3号男生可以找1号妹子
所以第三步最后的结果就是:
===============================================================================
四: 接下来是4号男生,很遗憾,按照第三步的节奏我们没法给4号男生腾出来一个妹子,我们实在是无能为力了……香吉士同学走好。
===============================================================================
这就是匈牙利算法的流程,其中找妹子是个递归的过程,最最关键的字就是“腾”字
其原则大概是:有机会上,没机会创造机会也要上
算法步骤(基于二分图)
- 如果题目初始的时候给出的是一副一般图,我们可以利用拓扑染色(2-SAT、种类并查集等等)的方式来让他变成一个二分图。
- 将二分图分成X部和Y部,我们现在的做法是通过X部去匹配Y部;
- 我们从1~cnt_X的去枚举,如果没有被对面的Y给匹配过的,我们试着让他去匹配对应的有链接边的Y部,如果对面的Y部对应的点y是没有被匹配的,或者链接对应y的x是可以换一个没有被匹配过的点的,那么我们就让x去换,并且让我们现在枚举的1~cnt_X去链接对应的点
- 在算法上的优化,我们不难发现,如果目前的1~cnt_Y中的某个y在这一回合的匹配中是被利用过了的,那么也就是说,后面的点都是已经被操作过(且为false的),说明我们不用再继续往下跑了,所以可以利用一个vis标记来进行优化。在枚举x的1~cnt_X的同时,我们进入“腾”这个操作的时候,就是需要去对1~cnt_Y进行vis清空操作。
算法步骤(基于链)
- 链上的匈牙利算法的算法结构并不特殊,只是对于建边的时候需要慎重考虑。
- 它是在一条链上的匹配,我们当去寻求匹配对象的时候,我们要尽可能的先把前面的link[x]覆盖,不然当我们匹配到后面的时候却已经被覆盖了,不是最优解。
- 所以,我们的建边尽可能是要从后面的结点指向前面的结点,例如i -> j(i > j)。
- 然后,我们1~N的枚举,当前的点一定是没有被匹配过的,因为操作3保证了。所以,我们直接去寻求匹配即可。
例题讲解(不按难度顺序排列)
Oil Skimming
HDU - 4185
这道题的精髓在于,i点和j点是成对匹配的。也就是说,当我们匹配上j点的时候,事实上,i点同时被j点匹配,所以我们一次操作的时候是需要赋值两次。
link[v] = u;link[u] = v;
也就是这样。
My Code
Taxi Cab Scheme
HDU - 1350
这道题就是一道很模版的匈牙利算法在链上的操作了。
有N个人,他们在特定的时间(hour : minute)要从地点(a, b)前往地点(c, d),然后呢,我们如果说前一个人i的出租车可以开回来再接j这个人,那么他们就可以少叫一辆出租车了。现在,题目想知道最少的需求的出租车的个数。
图文讲解 + 代码
过山车
HDU - 2063
匈牙利算法的模板题,这里给出主要的结构。理清楚,我们在哪个位置要清空X的内容,在哪个位置是要去清空Y部的内容,我这里并没有去用拆点或者使用memset()来逃避这两个问题。
bool match(int u)
{for(int i=head[u], v; ~i; i=edge[i].nex){v = edge[i].to;if(vis[v]) continue;vis[v] = true;if(!link[v] || match(link[v])){link[v] = u;return true;}}return false;
}
inline int Hungery()
{int ans = 0;for(int i=1; i<=M; i++) link[i] = 0;for(int i=1; i<=N; i++){for(int j=1; j<=M; j++) vis[j] = false;if(match(i)) ans++;}return ans;
}
这两行是主要的代码,要仔细看清哪里用的是X,哪里用的是Y。(N对应X,M对应Y)
棋盘游戏
HDU - 1281
看着题意很复杂,但是我们可以简化一下。
不能横竖给交叉了,实际上指的是每个对应的x坐标对应唯一的y坐标,我们基于这点去对于二维平面进行匈牙利算法即可。复杂度是,其中的源于匈牙利算法的复杂度。
判断是否是重要结点,可以自己想想看。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 105;
int N, M, K, head[maxN], cnt, cn_X, cn_Y;
vector<pair<int, int>> vt;
struct Eddge
{int nex, to;Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN * maxN];
inline void addEddge(int u, int v)
{edge[cnt] = Eddge(head[u], v);head[u] = cnt++;
}
int link[maxN];
bool vis[maxN];
inline bool match(int u)
{for(int i=head[u], v; ~i; i=edge[i].nex){v = edge[i].to;if(u == cn_X && v == cn_Y) continue;if(vis[v]) continue;vis[v] = true;if(!link[v] || match(link[v])){link[v] = u;return true;}}return false;
}
inline int Hungery()
{int ans = 0;for(int i=1; i<=M; i++) link[i] = 0;for(int i=1; i<=N; i++){for(int j=1; j<=M; j++) vis[j] = false;if(match(i)) ans++;}return ans;
}
inline void init()
{cnt = 0; cn_X = cn_Y = 0; vt.clear();for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{int Cas = 0;while(scanf("%d%d%d", &N, &M, &K) != EOF){init();for(int i=1, u, v; i<=K; i++){scanf("%d%d", &u, &v);addEddge(u, v);vt.push_back(MP(u, v));}int const_ans = Hungery(), Important = 0;for(int i=0; i<K; i++){cn_X = vt[i].first; cn_Y = vt[i].second;if(Hungery() < const_ans) Important++;}printf("Board %d have %d important blanks for %d chessmen.\n", ++Cas, Important, const_ans);}return 0;
}
匈牙利算法【匹配问题】相关推荐
- 匈牙利算法学习笔记_Python代码
学习华为上机测试题,遇见了下面题,很有意思,核心是匈牙利算法问题. 特此学习记录.资料均参考自网络. 匈牙利算法目的:找出两边最大的匹配的数量. 参考资料: https://blog.csdn.net ...
- 二分图匹配匈牙利算法DFS实现
1 /*==================================================*\ 2 | 二分图匹配(匈牙利算法DFS 实现) 3 | INIT: g[][]邻接矩阵; ...
- 二分图匹配的匈牙利算法
匈牙利算法,很绕,其实写起来也就一点点长度.. bool find(int a){int i,j;for(i=head[a];i;i=next[i]){j=to[i];//获得相邻的点if(!unab ...
- 利用匈牙利算法Hopcroft-Karp算法解决二分图中的最大二分匹配问题 例poj 1469 COURSES...
首先介绍一下题意:已知,有N个学生和P门课程,每个学生可以选0门,1门或者多门课程,要求在N个学生中选出P个学生使得这P个学生与P门课程一一对应. 这个问题既可以利用最大流算法解决也可以用匈牙利算法解 ...
- 二分匹配(匈牙利算法)
匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最 ...
- 二分图匹配之匈牙利算法
二分图的基本概念: 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于 ...
- [ACM_图论] The Perfect Stall 完美的牛栏(匈牙利算法、最大二分匹配)
描述 农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术.不幸的是,由于工程问题,每个牛栏都不一样.第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在她们 ...
- 二分图匹配--匈牙利算法
文章目录 二分图: 匹配 匈牙利算法 代码: 二分图: 二分图是一个无向图,点集分成子集X和Y,图中每一条边都是一边在X一边在Y 当且仅当无向图G的每一个回路次数都是偶数时(包括0),G就是一个二分图 ...
- 【匈牙利算法】【二分图匹配】【转载】趣写算法系列之--匈牙利算法
转载自:http://blog.csdn.net/dark_scope/article/details/8880547 [书本上的算法往往讲得非常复杂,我和我的朋友计划用一些简单通俗的例子来描述算法的 ...
- 【HDU - 5943】Kingdom of Obsession(数论,素数间隔结论,构造,思维,匈牙利算法,匹配问题)
题干: There is a kindom of obsession, so people in this kingdom do things very strictly. They name the ...
最新文章
- 微服务注册中心的选型和思考
- CUDA从入门到精通(零):写在前面
- 阿里巴巴云游戏平台荣获首届高新视频创新应用大赛一等奖
- 三层交换机有什么优势?
- 查询工资最低的3名员工的职工工号、姓名和收入_@打工人 | 关于工资条,这6个常识必须掌握,事关你的权益...
- HDU 6185 2017广西邀请赛:Covering(矩阵快速幂)
- 开课吧Java课堂:什么是流?如何运用字节流和字符流?
- 免费报名 | DataFunCon:自然语言处理论坛
- h5调用原生摄像头扫描二维码
- 用JavaScript+HTML实现双色球随机摇号效果
- html的个人简历边框,怎样制作简历模板边框
- deepspeech 1 (百度 2014 论文解读)
- 想要升级Big Sur 了?良心建议看看这个
- 12星座大全 ^__^
- 【THUSC2017】座位
- Excel 同一单元格显示不同颜色
- 【论文解读】NAACL 2021 对比自监督学习的上下文和一般句子表征:以篇章关系分析为例
- 右键删除或新增 Open Folder as Intellij IDEA Project
- 1.【Windows图标点击无反应修复
- 7款相见恨晚的资源网站,每个都百里挑一,送给正需要的你!