二分图匹配:匈牙利算法

本文来自:http://hi.baidu.com/the_centriest_magic/blog/item/ac83c11e07428517413417c6.html

 

 

 

 

囧囧地写个教程...

大家看之前其实可以先到百度百科那里了解一下http://baike.baidu.com/view/501092.htm

 

 


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

 

       Hungary,匈牙利算法。。。怎么也想不出这个算法跟匈牙利有什么联系的说。。是个叫匈牙利的人发明的?

算了,入正题吧。。

 

       首先,我们明确一个概念——增广轨(),这个增广轨跟网络流里的增广轨有点不同,这个增广轨其实应该叫做交错轨

我们来看看这个交错轨吧。。

 

       这条交错轨 从起点出发的这条边 不属于 目前已经求出来的匹配(为什么?看后面),然后从起点到达节点v1,

       显然,如果v1是一个不是原来已经求出来的匹配里的点,那么加上这个点,就可以让匹配数 1 了。

       如果v1已匹配了呢? 如果已经匹配了,那么我们就从v1出发,来到跟它匹配的那个点v2,

      

       到目前为止,我们从起点开始经过了两条边,一条是不属于匹配的(起点 - v1),一条属于匹配的(v1 - v2);

      

       接着从v2出发,找一个 还没有 被匹配的点v3,如果找到了,那么就可以加上这个点了,然后就可以让匹配数 1 了。

       这样我们找到了一条有3条边的路径,第一条是不属于匹配的,第二条是属于匹配的,第三条是不属于匹配的;

    然后,我们把属于匹配的变成不属于匹配的,把不属于的变成属于的,这个操作简称 反转

       显然,这样,属于匹配的边数就增加了1

       看到了吧,这就是交错轨了。。。显然交错轨,就是一条 未匹配,匹配,未匹配...这样交错出现的路径,然后把这个路径反转。

 

       那如果找不到呢没有被匹配的点呢?那么我们会找到一个属于匹配的点v3,我们从v2出发,来到v3

       那么,从v2v3这条边是不属于匹配的,那么我们到目前为止,有3条边,第一条是不属于匹配的,第二条是属于匹配的,

第三条也是不属于匹配的(这时还不可以翻转啊,因为后面有个v4跟他连着,你现在还没取消跟v4的关系,还不能就这么加1,

不然v3又跟v2连又跟v4,这样不行的.),然后从v3出发,就可以来到跟他匹配的点v4,然后,我们从v4开始,这样加入了第4条边,

这条边是属于匹配的。

       然后又从v4出发,像处理v2那样(这里我们就可以用递归了),找一个还没有 被匹配的 v5,找到了就加上这条(v4-v5)不属于匹配的

边, 找不到就找个已经匹配了的v5,然后加上从v4出发到v5的这样不属于匹配的边,再加上从v5到跟他匹配的v6这条已经匹配了的边,

然后再像处理v4那样处理v6。。。就这样一直递归下去,直到处理v2n(v2,v4,v6...)的点时,能找到一个不属于匹配的点v2n+1(v3,v5,v7...).

 

       (s - v1)         (v1 - v2)      (v2 - v3)   (v4 - v5)     (v5 - v6)    (v6 - v7) (v7 - t)

       不属于      属于   不属于  属于   不属于  属于  不属于

       按照这样,我们找到最后,要么就是找到了一条增广轨,然后翻转,匹配数加1,要么就是找不到了,算法也就结束了....

      

       所以,找交错轨,就是从一个节点开始,找一条不属于匹配的边,然后走,走到最后,走一条同样不属于匹配的边,走到终点,

这样找到一条交错轨,显然交错轨里,不属于匹配的边 会比 属于匹配的边 1 (为什么?画几个图出来试试看?前提是你已经清楚地明

白什么是交错轨),这样 把边 翻转一下 不属于变属于,属于变不属于。。这样属于匹配的边 就会比 不属于匹配的边 1了。。就这样,

匈牙利就是这样不断地找交错轨,让匹配加1,找不到时算法就结束了。。

 

[代码]

 

 

bool check(int t){
//找增广路的递归过程。
for
(int i=1;i<=m;i++){
if
(!used[i]&&g[t][i]){
used[i]=true;
if
(!link[i]||check(link[i])){
//这个就是判断Y集合里第i个点是否被匹配,是的话就递归去到跟它匹配的那个点。直到!link[i]为true为止.
link[i]=t;
return
true;
}
}
}

return
false;
}

int
Max(){
//这是主过程,从第i个开始寻找增广路,找到了匹配数+1,没找到就跳过。
int
ans=0;
for
(int i=1;i<=n;i++){
memset(used,false,sizeof(used));
if
(check(i))ans++;
}

return
ans;
}
其实用广搜来找增广路效率更高,不过用个递归来ms更容易理解。

 

 

 

参考资料:

1.http://www.matrix67.com/blog/archives/39 M67牛对匈牙利算法的理解。

2.http://www.fengzee.com/blog/article.asp?id=71 fengzee牛的重要程序整理里也有对这个算法的讲解

参考练习题:

1. http://www.rqnoj.cn/Problem_Show.asp?PID=141

2. http://www.rqnoj.cn/Problem_Show.asp?PID=192

 

 

 

ps:虽然写得很烂,但转载请注明出处,谢谢~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值