什么是匹配

匹配:在图论中,一个「匹配」是一个边的集合,其中任意两条边都没有公共顶点。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。

二分图的匹配:给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

匈牙利算法

前面的染色法是判断一个图是否是二分图;
匈牙利算法就是在是二分图的基础上,求二分图的最大匹配的算法。

二分图将顶点分成两个集合A和B,集合A和B内部之间没有边,A中的点到B中的点存在边。(A中的点x到B中的某些点之间存在边,可能一条,可能多条,也可能没有)
最大匹配就是尽可能的可以多选择边,使得A中的点和B中的点匹配,且只能一对一匹配,即匹配过的不能再匹配,使任意两条边都没有公共顶点。

二分图一般都是无向图,双向匹配,我们在试图匹配的时候,只从一个方向到另一个方向匹配就行:以下做题及代码都是假设从集合A出发到集合B中区匹配的。因此,虽然是无向图,我们可以只存储从A->B的边,而不存储B->A的边,这样可以自动的将顶点分成两个集合,而且两个集合内结点的编号都可以从1开始

匹配过程

如果你想找的妹子已经有了男朋友,
你就去问问她男朋友,
你有没有备胎,
把这个让给我好吗?
他能让就让,
要是让不了,他们就是真爱。
你就找你的下一个备胎。

假设此时已暂时有匹配关系a1----b1,a2----b2,A中的顶点a1有指向b1、b2的点,a2只有指向b2的边,a3有指向b1、b3、等……的边,我们以A中的顶点a3的匹配为例:

假设已暂时有匹配关系a1----b1,a2----b2,那么a2就不能有指向b1的边,否则就不符合匈牙利算法核心1。

下面分析过程:

给a3找匹配对象,a3先去找b1,发现b1已经被匹配了,但是此时a3不是去找b3,而是让b1去找他的匹配对象a1,看看a1能不能找别的点匹配,此时回到了给a1找匹配对象的问题,但是匹配对象b1被禁了,所以a1不能再去找b1,接下来去找b2,b2没被禁,可以找,但是b2已经有了匹配对象a2,然后再去问a2能不能换个匹配对象,此时又回到了给a2找匹配对象的问题上与此同时点b1、b2都被禁了因为b1点是a3点想要的,b2点是a1点想要的,都被待定了,但是a2已经没有再往后退的选择了,此时a2和b2是真爱,永久匹配,被锁定。然后再往回告诉a1,b2不行,你再找下一位吧,但是a1也是没有了回退的余地,所以a1和b1也是真爱,永久匹配,被锁定。然后告诉a3,b1不行,你再找下一位吧,然后a3开始找b3……

上面的匹配可以用match数组,被禁可以用st数组表示状态,是否锁定可以用vis数组表示
match[j]=i 表示集合A中的 点i 和集合B中的 点j 暂时匹配
st[j]=true表示顶点 j 暂时被禁
vis[j]=true表示match[i]=j中的 i 和 j 是真爱,后面再来的顶点不要再想着占 顶点j 了。

假设a1只有指向b1、b2的边,a2只有指向b1、b2的边,a3有指向b1、b2、b3的边
先给a1选择,match[a1]=b1;
然后a2选择,发现b1被占用,然后强迫a1让位,此时有match[a1]=b2; match[a2]=b1;
然后a3选择,b1被占用,然后a3强迫a2让位,a2不能选择b1,只能选择b2,然后a2强迫a1让位,此时a1不能选择b1、b2,a1没有回退的余地,a1和b2被锁定,把消息告诉a2后,a2也没有回退的余地,a2和b1被锁定。
当a3找上b2时,直接告诉a3,b2被锁定了,没戏,你找下一位吧。
接下来a3找到b3,有match[a3]=b3;

再举这样一个例子是想说明匈牙利算法的核心:

  1. 先匹配的都是暂时的,后匹配的才是最有可能获得的,因为先匹配的都要尽可能的让后匹配的,除非先匹配的让到了退无可退的地步。
  2. 这是一个递归的过程,递归结束的那次匹配并不仅仅是因为这次匹配的顶点出度为1,只有一条边伸向另一个集合,这只是其中一种情况,一般情况则是这个顶点已经到了退无可退的地步,不能再给别的顶点让了,再让自己就没有了。

题目描述

给定一个二分图,其中左半部集合包含n1个点(编号1-n1),右半部集合包含n2个点(编号1-n2),二分图共包含m条边。

数据保证任意一条边的两个端点都不可能在同一部分中。

请你求出二分图的最大匹配数。

输入格式
第一行包含三个整数 n1、 n2 和 m。

接下来m行,每行包含两个整数u和v,表示左半部点集中的点u和右半部点集中的点v之间存在一条边。

输出格式
输出一个整数,表示二分图的最大匹配数。

数据范围
1≤n1,n2≤500,
1≤u≤n1,
1≤v≤n2,
1≤m≤105

输入样例:

2 2 4
1 1
1 2
2 1
2 2

输出样例:

2

算法实现

#include <iostream>
#include <cstring>using namespace std;#define read(x) scanf("%d",&x)
const int N=510,M=1e5+10;  //虽是无向图,但是存储单方向的边A->B即可,到时候从A找点匹配B
int h[N],e[M],ne[M],idx;
int match[N];
bool vis[N],st[N];
//三个数组维护的含义见上面或下面
int n1,n2,m;
"n1+n2是总顶点数,n1是A集合中顶点数,n2是B集合中顶点数,由题目要求限制"void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}int find(int v)
{for (int i=h[v];~i;i=ne[i]) {int j=e[i];if (!vis[j] && !st[j]) {if (match[j]==0) {match[j]=v; return true;}else {st[j]=true;if (find(match[j])) {match[j]=v; return true;}else vis[j]=true;}//此时说明B堆中的j结点与A堆中的结点match[j]是板上钉钉的匹配了,不能再改了,}    //所以后面的点再匹配就不用再找我了,}return false;
}int main()
{memset(h,-1,sizeof h);read(n1),read(n2),read(m);//A堆顶点数n1,B堆顶点数n2,边数mint a,b;while (m--) {read(a),read(b);add(a,b);"两个堆中结点编号重复,这里无影响,也可以add(a,n1+b);"}int res=0; //统计最大匹配的边数for (int i=1;i<=n1;i++) {memset(st,false,sizeof st); //每次初始化一下st数组,重新禁止一些匹配。if(find(i)) res++; }printf("%d",res);return 0;
}

match数组:
遍历A堆,存储A->B的边。此时match[i]=j;表示B堆中顶点i与A堆中的顶点j匹配了,由于图中顶点的下标都是从1开始的,所以当match[i]=0时,表示还没有匹配到。

st数组:
st数组的作用是当一个左边的点匹配到右边某个已经有对象的点时,右边的点对应的那条边的左边的点,去寻在新的“对象“时,不再去找原来的右边的那个点,由于多次递归,每次递归前禁止的点都要记录上,所以创建一个st数组,每次开始新的匹配时,又要重置st数组为false,重新禁止

vis数组:
vis数组用于记录那些B堆中没有选择的点,当B堆中的点只有最后一种情况和A堆中的点匹配时,它就被锁定了,所以它也就没有find(match[ ])的必要了。

初级版本,想用 if (!vis[j] && match[j]!=x) 中的 match[j]!=x 代替st数组的功能,但由于每次递归都会禁止一个j,而这样写只会禁止当前的j,对之前的j没有作用了,容易造成死循环。爆栈。

 bool find(int x)
{for (int i = h[x]; i != -1; i = ne[i])  {int j=e[i];if (!vis[j] && match[j]!=x) {if (match[j]==0) {match[j]=x; return true;}else {if (find(match[j])) {match[j]=x; return true;}else vis[j] = true;}                     }}return false;
}

第5行判断处不一样,这是错误的。

二分图的匹配——匈牙利算法相关推荐

  1. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  2. 二分图匹配--匈牙利算法

    文章目录 二分图: 匹配 匈牙利算法 代码: 二分图: 二分图是一个无向图,点集分成子集X和Y,图中每一条边都是一边在X一边在Y 当且仅当无向图G的每一个回路次数都是偶数时(包括0),G就是一个二分图 ...

  3. nyoj-239 月老的难题 (二分图匹配—匈牙利算法 网络流—Dinic算法)

    月老的难题 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘. 现在,由于一些原因,部分男孩与女孩可能结成幸福的一 ...

  4. 二分图匹配匈牙利算法DFS实现

    1 /*==================================================*\ 2 | 二分图匹配(匈牙利算法DFS 实现) 3 | INIT: g[][]邻接矩阵; ...

  5. python最长匹配_二分图最大匹配:匈牙利算法的python实现

    二分图匹配是很常见的算法问题,一般用匈牙利算法解决二分图最大匹配问题,但是目前网上绝大多数都是C/C++实现版本,没有python版本,于是就用python实现了一下深度优先的匈牙利算法,本文使用的是 ...

  6. 二分图匹配——匈牙利算法

    匈牙利算法 什么是匈牙利算法 匈牙利算法是一种在多项式时间内求解任务分配问题的组合优化算法,并推动了后来的原始对偶方法.美国数学家哈罗德·库恩于1955年提出该算法.此算法之所以被称作匈牙利算法,是因 ...

  7. Codevs 1222 信与信封问题 二分图匹配,匈牙利算法

    题目: http://codevs.cn/problem/1222/ 1222 信与信封问题   时间限制: 1 s   空间限制: 128000 KB   题目等级 : 钻石 Diamond 题解 ...

  8. 二分图匹配-匈牙利算法

    说到二分,我们就很容易想到二分查找算法,今年下半年--,今天所介绍的二分图和二分查找没有太大的联系,我们先来看一下它的定义: 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图 ...

  9. 二分图的最大匹配—匈牙利算法

    [基本概念]: 二分图: 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分 ...

最新文章

  1. 退出页面 数据保留_设计师常用的数据分析指标
  2. 在Python这条路上踩过的坑(1)
  3. 【theano-windows】学习笔记十六——深度信念网络DBN
  4. OpenJudge NOI 1.7 14:大小写字母互换
  5. 博弈论 ----- Nim游戏
  6. 联想第三季:PC+时代的航母启航?
  7. Mac实践--MAC搭建FTP服务器
  8. ubuntu配置vsftpd记录
  9. java jbutton 不显示_java JButton显示问题
  10. XSS labs 闯关大合集
  11. Flutter开发之常用Widget学习
  12. python list 对时间排序小结。
  13. 30天自制操作系统第1天 - Hello World
  14. UESTC ACM训练题二
  15. springboot框架下的实时消息推送
  16. 谁是程序员的祖师爷?
  17. IntelliJ IDEA详细配置图解,挖掘更多的功能!
  18. linux系统一直循环登录界面,Ubuntu卡在登陆界面无限循环的问题
  19. NPDP笔记 第一章 战略
  20. dancer.js_与轻量级的Perl Web应用程序框架Dancer一起旋转

热门文章

  1. Web网页如何实现QQ好友,QQ空间,微博分享
  2. CSS3实现3D立体效果
  3. [读论文]点云表面重建: SDF, TSDF, MLS, RBF
  4. cocos creator微信小游戏截图
  5. Enscape 代理服务器和防火墙设置
  6. BurpSuite 基本使用之暴力破解
  7. 解决ERROR: distribution port 25672 in use by another node: rabbit@
  8. mac 提示文件已损坏 解决办法
  9. C#Base64编码
  10. 那些android你需要知道的事