简介
设G=(V,E)是一个无向图。如顶点集V可分割为两个互不相交的子集V1,V2,选择这样的子集中边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,|V1|<=|V2|且匹配数|M|=|V1|则称此匹配为完全匹配,也称作完备匹配。特别的当|V1|=|V2|称为完美匹配。
简易入门教程
趣写算法系列之–匈牙利算法
http://blog.csdn.net/dark_scope/article/details/8880547
相关概念
M是G的一个匹配。
(M:{(x2,y4),(x3,y1),(x4,y3),(x6,y5)})
M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。例如:x1-y1-x3-y2
M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。如x2,x3,x4,x6,y1,y3,y4,y5都属于M-饱和点,而其它点都属于非M-饱和点。
M-可增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。例如:x1-y1-x3-y2
求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的时间复杂度为边数的指数级函数。因此,需要寻求一种更加高效的算法。下面介绍用增广路求最大匹配的方法(称作匈牙利算法,匈牙利数学家Edmonds于1965年提出)。
由增广路的定义可以推出下述三个结论:
1-P的路径个数必定为奇数,第一条边和最后一条边都不属于M。
2-将M和P进行取反操作可以得到一个更大的匹配M'。
3-M为G的最大匹配当且仅当不存在M的增广路径。
算法轮廓
⑴置M为空
⑵找出一条增广路径P,通过异或操作获得更大的匹配M'代替M
⑶重复⑵操作直到找不出增广路径为止
在这个过程中,如何快速找出增广路呢?
这里提供一种方法:如下图所示,找一个非M-饱和点,画一棵M-交错树,如果能找到,就重复(2),不能找到算法就可以结束咯~
代码
//伪代码
bool 寻找从k出发的增广路
{
for (k的所有邻接点j)
{
if (j不在增广路上)
{
把j加入增广路;
if (j是非-M饱和点 或者 从j的对应项出发有可增广路)
{
修改j的对应项为k;
返回true;
}
}
}
返回false;
}
void 匈牙利hungary()
{
for i->1 to n
{
if (则从i出发有增广路)
匹配数++;
}
输出 匹配数;
}
//C++代码
//b[j]表示j的匹配对象,0表示未匹配
bool find(int x)
{
for (int j = 1;j <= n;j++)
{
if (arc[x][j]&& !v[j])
{
v[j]=1;
if (b[j]==0 || find(b[j]))
{
b[j] = x;
return true;
}
}
}
return false;
}
for (int i = 1; i <= m; i++)
{
memset(v,0,sizeof(v)); //注意每一次v都要清空,每一次找增广路都是一次新的开始
if (find(i)) ans += 1;
}
对递归的一些理解
如图G,比如我们现在进行find(x1);
x1会去找y1,但是发现b[y1]不是0了,于是递归进行find(x3),此时v[y1]=1,已经被标记,所以x3不能再找y1了,于是它找到了y2(当然,如果y2已经名花有主,那么再次递归,让y2的主另寻他欢)
在这个过程中,发生了这样的事情:我们试图将(x1, y1),(x3, y2)增加到M中,而将(x3,y1)从M中删除——这正是在试图用M’来代替M,一旦找到增广路,返回true,则递归收缩回来,逐步执行b[j]=x来修改边,如果找不到增广路,返回false,则一切的修改都不会执行。