图论——二分匹配

主要的知识细节都是从这篇博客看到的: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匹配,返回trueif m1匹配成功
            除去n1和m1,考察与m1匹配的点是否存在增广路径。存在,则让m1和n1匹配,返回true,否则返回falseend

代码实现:

//以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))

Tablesy0y1y2y3
x06535
x12435

计算的结果如上表所示,可知,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的所有取值为:

Tablesy1y2y3
x0212
x1213
x2243

可知d的取值为1,则S集合-1,{y0,y4}集合+1。结果如下图:
这里写图片描述

变化之后增加了x0->y2和x1->y2这两条路。
那么找到的增广路即为:x2->y0->x1->y2。则匹配结果如下所示

这里写图片描述

6.寻找x3的匹配节点
以x3为起点寻找增广路径,不存在。那么S={x3},T=Y,故d的取值为

Tablesy0y1y2y3y4
x313444

故d=1则L{x3}-1=5;
这里写图片描述
则寻找的增广路径为:x3->y0->x2,非法路径,故S={x2,x3},T={y1,y2,y3,y4};
那么d的取值为:

Tablesy1y2y3y4
x21327
x32333

故d的取值为1;那么图更新{S}-1,{Y-T}+1为:

这里写图片描述
增加了一条路径为x2->y1,则x3的增广路径为x3->y0->x2->y1于是新的匹配为:

这里写图片描述

7.寻找x4节点的匹配路径
以x4为起点寻找增广路经,不存在。S={x4},T={};
故d的可能取值为:

Tablesy0y1y2y3y4
x424344

故d的取值为2;故图变化为:
这里写图片描述

然后找到的路径为:x4->y0->x3不满足增广路要求。S={x3,x4},T={y1,y2,y3,y4},于是d的取值为

Tablesy1y2y3y4
x31222
x42122

于是d = 1,图中增加两条路x3->y1,x4->y2更新后的图为:
这里写图片描述

然后发现还是找不到增广路经。。。所以得继重复上面的方法,就可以得出正确的解了。

代码实现

——2017.3.9 代码之后再补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值