二分图匹配之最佳匹配——KM算法

今天也大致学了下KM算法,用于求二分图匹配的最佳匹配。

何为最佳?我们能用匈牙利算法对二分图进行最大匹配,但匹配的方式不唯一,如果我们假设每条边有权值,那么一定会存在一个最大权值的匹配情况,但对于KM算法的话这个情况有点特殊,这个匹配情况是要在完全匹配(就是各个点都能一一对应另一个点)情况下的前提。

自然,KM算法跟匈牙利算法有相似之处。

其算法步骤如下:

1.用邻接矩阵(或其他方法也行啦)来储存图,注意:如果只是想求最大权值匹配而不要求是完全匹配的话,请把各个不相连的边的权值设置为0

2.运用贪心算法初始化标杆。

3.运用匈牙利算法找到完备匹配。

4.如果找不到,则通过修改标杆,增加一些边。

5.重复3,4的步骤,直到完全匹配时可结束。

一言不合地冒出了个标杆??标杆是什么???

在解释这个问题之前,我们先来假设一个很简单的情况,用我们人类伟大的智能思维去思考思考。

二分图匹配之最佳匹配——KM算法
如上的一个二分图,我们要求它的最大权值匹配(最佳匹配)

既然要最大,我们肯定会有个贪心的思想,就是尽量让权值较大的边连线。

于是乎,我们连了AD这条15的线。

对于F点,我们发现连接它的三个点ABC中,CF权值是最大的,我们也对它进行连线,此时权值总和w=15+10=25;

现在问题来了,对于E点很明显只有BE这条可连线段,但是它的权值只有6!

对比了一下发现,AE的权值却是12!

如果我们假设连了AD和BE,那么这两条线的权值和为21;

但如果我们连了AE和BD,却发现权值和为26!它们相差了5!

所以我们发现贪心的策略是错误的,但辛亏了我们人类的智能,继续通过对比C点和其他点的边的连线权值,发现没有更大的情况了,此时权值和是AE+BD+CF=36

回顾上面我们发现,我们采用的是贪心连最大值,然后再对已连边和未连边的权值比较差值,然后进行更改错误。

KM算法个人理解也大致是如此的。

所以,根据贪心策略,我们一开始要对边权值最大的进行连线,那问题就来了,我们如何让计算机知道该点对应的权值最大的边是哪一条?或许我们可以通过某种方式

记录边的另一端点,但是呢,后面还要涉及改边,也就是要计算差值,而这个记录端点无法实现,于是KM采用了一种十分巧妙的办法(也是KM算法思想的精髓):

添加标杆(顶标)

是怎样子呢?我们对左边每个点Xi和右边每个点Yi添加标杆Cx和Cy。

其中我们要满足Cx+Cy>=w[x][y](w[x][y]即为点Xi、Yi之间的边权值)

对于一开始的初始化,我们对于每个点分别进行如下操作

Cx=max(w[x][y]);

Cy=0;

二分图匹配之最佳匹配——KM算法

然后,我们可以进行连边,即采用匈牙利算法,只是在判断两点之间是否有连线的条件下,因为我们要将最大边进行连线,所以原来的条件w[x][y]==0换成了

Cx+Cy==w[x][y]

此时,有一个新的名词——相等子图。

因为我们通过了巧妙的处理让计算机自动连接边权最大的边,换句话说,其他边计算机就不会连了,也就“不存在”这个图中,但我们可以随时加上这些“不存在”图中的边。此时这个图可以认为是原图的子图,并且是等效。

这样,计算机在枚举右边的点的时候,满足以上条件,就能够知道这条边是我们要连的最大的边,就能进行连边了。

于是乎我们连了AD。

接下来就尴尬了,计算机接下来要连B点的BD,但是D点已经和A点连了,怎么办呢???

根据匈牙利算法,我们做的是将A点与其他点进行连线,但此时的子图里“不存在”与A点相连的其他边,怎么办呢??

为此,我们就需要加上这些边!

但是,那会不会使得边权值变小呢?

这个就需要比较。

很明显,我们添边,自然要加上不在子图中边权最大的边,也就是和子图里这个边权值差最小的边。

于是,我们再一度引入了一变量d,d=min{Cx[i]+Cy[j]-w[i][j]}

其中,在这个题目里Cx[i]指的是A的标杆,Cy[j]是除D点(即已连点)以外的点的标杆。

随后,对于原先存在于子图的边AD,我们将A的标杆Cx[i]减去d,D的标杆Cy[d]加上d。

这样,这就保证了原先存在AD边保留在了子图中,并且把不在子图的最大权值的与A点相连的边AE添加到了子图。

因为计算机判断一条边是否在该子图的条件是其两端的顶点的标杆满足 

Cx+Cy==w[x][y]

对于原先的边,我们对左端点的标杆减去了d,对右端点的标杆加上了d,所以最终的结果还是不变,仍然是w[x][y]。

对于我们要添加的边,我们对于左端点减去了d,即Cx[i]=Cx[i]-d;为方便表示我们把更改后的的Cx[i]视为Cz[i],即Cz[i]=Cx[i]-d;

对于右端点,我们并没有对其进行操作。那这条我们要添加边的两端点的标号是否满足Cz[i]+Cy[j]=w[i][j]?

因为Cz[i]=Cx[i]-d;d=Cx[i]+Cy[j]-w[i][j];

我们把d代入左式可得Cz[i]=Cx[i]-(Cx[i]+Cy[j]-w[i][j]);

化简得Cz[i]+Cy[j]=w[i][j]。

满足了要求!即添加了新的边。

要注意的是这里我们只是对于一条边操作,当我们添加了几条边,要进行如上操作时,要保证原先存在的边不消失,那么我们就要先求出了d,然后

对于每个连边的左端点(记作集合S)的每个点的标号减去了d之后,然后连边的右端点(记作T)加上d,这样就保证了原先的边不消失啦~

实际上这就是一直在寻找着增广路,通过不断修改标杆进行添边实现。

接下来就继续着匈牙利算法,直到完全匹配完为止。

至此,我们再回顾KM算法的步骤:

 1.用邻接矩阵(或其他方法也行啦)来储存图。

2.运用贪心算法初始化标杆。

3.运用匈牙利算法找到完备匹配。 

4.如果找不到,则通过修改标杆,增加一些边。 

5.重复3,4的步骤,直到完全匹配时可结束。 

是不是清楚了许多??

因为二分图是网络流的一种特殊情况,在网络流里我们是通过不断的SPFA找到费用最大(小)的路径进行通流,跟这个有点类似。

如果我们要求边权值最小的匹配呢???

我们可以把边权值取负值,得出结果后再取相反数就可以了。

至于为什么,正负大小相反了嘛~

至此,这大概是我个人的一点点理解了,希望对您有所帮助。

若有不当之处还请大家指出。

QwQ代码明天再贴

上一篇:javascript – 保持圈子不重叠


下一篇:深入理解Java之线程池