主要的知识细节都是从这篇博客看到的:http://www.renfei.org/blog/bipartite-matching.html
无权二分图
增广路:从一个未匹配的节点出发,交替经过匹配的路径和未匹配路径,直到下一个节点是未匹配点为止。
举个例子:
如图所示,1和c匹配,2和a匹配。那么对于节点3开始的增广路径即为3->a->2 。。。啊呀,这个例子不好,增广路径就是3->b。。。基础知识你们还是参看上面的那篇博客吧,讲的真的很详细,我也不想在画图了。。。下面讲一下代码怎么实现。
如果你看了上篇那篇博客,那么你应该知道增广路是上面啦。
增广路特点1:如果去除增广路的前两个节点,那么剩下的路径依然是增广路,这个特点就可以让我们在代码中使用递归去寻找增广路。
增广路特点2:增广路中的每个节点只能出现一次。
增广路特点3:增广路反转之后,匹配数加一。
假设有两组节点,分别有n和m个。
寻找以节点
n1
为起点是否存在增广路的伪代码:
for m1 属于 m
if m1与n相连并且节点m没有使用过
if m1没有匹配 //上面的例子就是
m1与n1匹配,返回true。
if m1匹配成功
除去n1和m1,考察与m1匹配的点是否存在增广路径。存在,则让m1和n1匹配,返回true,否则返回false。
end
代码实现:
//以t节点为起点,寻找增广路径,如果存在返回值为true
bool augmentPath(int t){
int i;
for(i=0;i<Mnum;i++){
//如果节点m没有使用
if(!useif[i]&&graph[t][i]!=INF){
useif[i] = 1;
if(matched[i]==0||augmentPath(i)){
matched[t] = i;
return true;
}
}
}
return false;
}
求二分图的最大匹配
//求节点的最大匹配数
int maxMatch(){
int i;
int matchNum = 0;
for(i = 1;i<=Mnum;i++){
memset(useif,0,sizeof(useif));
if(augmentPath(i)){
matchNum++;
}
}
return matchNum;
}
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大匹配数 = 最大独立数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
有权二分图
KM算法
KM算法
要点在于标杆的使用,下面讲一下标杆的作用。
先说一下标杆的作用:在以x为起点寻找增广路的时候,y要满足L(x)+L(y)==weigh(x,y);也就是上面伪代码第二行需要满足的条件不止是x和y相连,才能加入到增广路经。不理解没有关系,其实其中还要设计标杆L的更新,内涵贪心思想。直接看例子:
1.初始化图的邻接矩阵
2.初始化标杆。我们让标杆初始化成其相连的边的最大值,那么保留下来的边也就只有如图所示的五条线,易知,这个图是不满足完美匹配的特点的。
3.第一步从x0开始寻找增广路。
虽然x0到集合Y的所有节点中都有边相连,但是要满足L(x0)+L(y)==weight(x0,y),所以只有y4满足条件,那么x0->y4就是一条增广路径,翻转之后得到x0的匹配节点为y4。
4.寻找x1的匹配节点,更新标号。
如上图,和x0一样,虽然x1与Y中所有的节点相连,但是要满足L(x1)+L(y)==weigh(x1,y)的节点,只有y4。根据匈牙利算法,那么会将y4加入到x1的增广路径中,然后继续寻找x0的增广路经,因为也只有y4满足x0的增广路径,所以,x1寻找增广路径会失败。然后就需要更新标号,也就是加入新的路径到图中。
如何更新标号?
目标是让x0和x1与Y相连的两个点的权值之和最大。有两种方案:
方案1:让x0的标号减少d1,使得x0到Y集合加入一条边yi(这条边是x0到Y集合的第二长的边),就可以找到增广路经x1->y4->x0->yi;则匹配路径为x1->y4和x0->yi,则权值之和为8+9-d1;
方案2:让x1的标号减少d2,使得x1到Y集合加入一条边yi(这条边是x1到Y集合的第二长的边),就可以找到增广路径x1->yi;则匹配路径为x1->yi和x0->y4,则权值之和为8-d2+9
两个方案,应该取权值的最大值,也就是d=min(d1,d2);
如果设S={x0,x1};T={y0,y1,y2,y3};那么9-d1就是x0到T的最大值,8-d2就是x1到T的最大值,那么d = min(L(s)+L(t)-weigh(s,t))
Tables | y0 | y1 | y2 | y3 |
---|---|---|---|---|
x0 | 6 | 5 | 3 | 5 |
x1 | 2 | 4 | 3 | 5 |
计算的结果如上表所示,可知,d =d2 = 2;更新之后的图如下所示
这种更新方式的问题所在在于不能完全的满足寻找最佳匹配的特点。只是权值在一定程度上满足而已,于是我们采用另一种更新策略,统一之间提出的前两种。
如下图,将S集合中的元素-d,Y-T集合中的元素+d
第二种更新方式肯定是对的,关于证明我也没有找到,记住算了。反正这种思想我也没见过有什么推广。
5.寻找x2的匹配节点
x2寻找到的第一条路径为x2->y0->x1->y4->x0结尾是匹配节点,所以不满足条件。
此时S={x0,x1,x2},T=Y-{y0,y4}={y1,y2,y3};
d的所有取值为:
Tables | y1 | y2 | y3 |
---|---|---|---|
x0 | 2 | 1 | 2 |
x1 | 2 | 1 | 3 |
x2 | 2 | 4 | 3 |
可知d的取值为1,则S集合-1,{y0,y4}集合+1。结果如下图:
变化之后增加了x0->y2和x1->y2这两条路。
那么找到的增广路即为:x2->y0->x1->y2。则匹配结果如下所示
6.寻找x3的匹配节点
以x3为起点寻找增广路径,不存在。那么S={x3},T=Y,故d的取值为
Tables | y0 | y1 | y2 | y3 | y4 |
---|---|---|---|---|---|
x3 | 1 | 3 | 4 | 4 | 4 |
故d=1则L{x3}-1=5;
则寻找的增广路径为:x3->y0->x2,非法路径,故S={x2,x3},T={y1,y2,y3,y4};
那么d的取值为:
Tables | y1 | y2 | y3 | y4 |
---|---|---|---|---|
x2 | 1 | 3 | 2 | 7 |
x3 | 2 | 3 | 3 | 3 |
故d的取值为1;那么图更新{S}-1,{Y-T}+1为:
增加了一条路径为x2->y1,则x3的增广路径为x3->y0->x2->y1于是新的匹配为:
7.寻找x4节点的匹配路径
以x4为起点寻找增广路经,不存在。S={x4},T={};
故d的可能取值为:
Tables | y0 | y1 | y2 | y3 | y4 |
---|---|---|---|---|---|
x4 | 2 | 4 | 3 | 4 | 4 |
故d的取值为2;故图变化为:
然后找到的路径为:x4->y0->x3不满足增广路要求。S={x3,x4},T={y1,y2,y3,y4},于是d的取值为
Tables | y1 | y2 | y3 | y4 |
---|---|---|---|---|
x3 | 1 | 2 | 2 | 2 |
x4 | 2 | 1 | 2 | 2 |
于是d = 1,图中增加两条路x3->y1,x4->y2更新后的图为:
然后发现还是找不到增广路经。。。所以得继重复上面的方法,就可以得出正确的解了。
代码实现
——2017.3.9 代码之后再补