二分匹配+加权

本文详细介绍了二分图匹配算法,包括最大匹配、最小覆盖、最大独立集等问题,并提供了匈牙利算法和Hopcroft-Karp算法的解析。还探讨了加权二分图的最优匹配问题,通过Kuhn-Munkres算法实现,以及如何调整算法以求解最小权值匹配。附带了加权二部图的C语言代码实现。
摘要由CSDN通过智能技术生成

  前段时间为了省赛,我专门花了半个月来“专研”二部图,目前对二部图还是有一点点心得,所以就记录下来,希望对某些人有用。

  开始我对二部图一窍不通,于是就在网上找资料,认真看完了各种资料,有一种感触:关于最大匹配问题,网上写的是挺好的,有深搜和广搜算法,很精辟;但是关于加权二部图,网上只有思想,没有具体实现代码,如果让一个一开始不知道二部图的算法的人去实现这个算法,还是有一定难度,所以决定写一点东西。

  首先对各种二部图做个简单的介绍吧(这些资料是我整合网上的)

 

 二分图匹配算法总结
二分图最大匹配的匈牙利算法
二分图是这样一个图,它的顶点可以分类两个集合X和Y,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y。
最大匹配: 图中包含边数最多的匹配称为图的最大匹配。
完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。
最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数
最小路径覆盖:
用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。
最大独立集问题:
在N个点的图G中选出m个点,使这m个点两两之间没有边.求m最大值.
如果图G满足二分图条件,则可以用二分图匹配来做.最大独立集点数 = N - 最大匹配数
二分图最大匹配问题的匈牙利算法:
#define N 202
int useif[N];   //记录y中节点是否使用
int link[N];   //记录当前与y节点相连的x的节点
int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0
int gn,gm;    //二分图中x和y中点的数目
int can(int t)
{
    int i;
    for(i=1;i<=gm;i++)
    {
       if(useif[i]==0 && mat[t][i])
       {
           useif[i]=1;
           if(link[i]==-1 || can(link[i]))
           {
              link[i]=t;
              return 1;
           }
       }
    }
    return 0;
}
int MaxMatch()
{
    int i,num;
    num=0;
    memset(link,0xff,sizeof(link));
    for(i=1;i<=gn;i++)
    {
       memset(useif,0,sizeof(useif));
       if(can(i)) num++;
    }
    return num;
}

(我还在网上找到了这个代码的具体流程(附在文章末),我还是建议大家自己亲自画一下这个流程图,以便自己理解。)
算法思想:
算法的思路是不停的找增广轨, 并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就是说这条由图的边组成的路径, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没有被选择过.这样交错进行,显然他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有的边进行"反色",容易发现这样修改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明,当不能再找到增广轨时,就得到了一个最大匹配.这也就是匈牙利算法的思路.
一、二分图最大匹配
    二分图最大匹配的经典匈牙利算法是由Edmonds在1965年提出的,算法的核心就是根据一个初始匹配不停的找增广路,直到没有增广路为止。
匈牙利算法的本质实际上和基于增广路特性的最大流算法还是相似的,只需要注意两点:
(一)每个X节点都最多做一次增广路的起点;
(二)如果一个Y节点已经匹配了,那么增广路到这儿的时候唯一的路径是走到Y节点的匹配点(可以回忆最大流算法中的后向边,这个时候后向边是可以增流的)。
    找增广路的时候既可以采用dfs也可以采用bfs,两者都可以保证O(nm)的复杂度,因为每找一条增广路的复杂度是O(m),而最多增广n次,dfs在实际实现中更加简短。
二、Hopcroft-Karp算法
    SRbGa很早就介绍过这个算法,它可以做到O(sqrt(n)*e)的时间复杂度,并且在实际使用中效果不错而且算法本身并不复杂。
    Hopcroft-Karp算法是Hopcroft和Karp在1972年提出的,该算法的主要思想是在每次增广的时候不是找一条增广路而是同时找几条不相交的最短增广路,形成极大增广路集,随后可以沿着这几条增广路同时进行增广。
    可以证明在寻找增广路集的每一个阶段所寻找到的最短增广路都具有相等的长度,并且随着算法的进行最短增广路的长度是越来越长的,更进一步的分析可以证明最多只需要增广ceil(sqrt(n))次就可以得到最大匹配(证明在这里略去)。
    因此现在的主要难度就是在O(e)的时间复杂度内找到极大最短增广路集,思路并不复杂,首先从所有X的未盖点进行BFS,BFS之后对每个X节点和Y节点维护距离标号,如果Y节点是未盖点那么就找到了一条最短增广路,BFS完之后就找到了最短增广路集,随后可以直接用DFS对所有允许弧 (dist[y]=dist[x]+1,可以参见高流推进HLPP的实现)进行类似于匈牙利中寻找增广路的操作,这样就可以做到O(m)的复杂度。
    实现起来也并不复杂,对于两边各50000个点,200000条边的二分图最大匹配可以在1s内出解,效果很好:)
三、二分图最优匹配
    二分图最优匹配的经典算法是由Kuhn和Munkres独立提出的KM算法,值得一提的是最初的KM算法是在1955年和1957年提出的,因此当时的KM算法是以矩阵为基础的,随着匈牙利算法被Edmonds提出之后,现有的KM算法利用匈牙利树可以得到更漂亮的实现。
    KM 算法中的基本概念是可行顶标(feasible vertex labeling),它是节点的实函数并且对于任意弧(x,y)满足l(x)+l(y)≥w(x,y),此外一个概念是相等子图,它是G的一个生成子图,但是只包含满足l(xi)+l(yj)=w(xi,yj)的所有弧(xi,yj)。
    有定理:如果相等子图有完美匹配,那么该匹配是最大权匹配,证明非常直观也非常简单,反设其他匹配是最优匹配,它的权必然比相等子图的完美匹配的权要小。
    KM算法主要就是控制了怎样修改可行顶标的策略使得最终可以达到一个完美匹配,首先任意设置可行顶标(如每个X节点的可行顶标设为它出发的所有弧的最大权,Y节点的可行顶标设为0),然后在相等子图中寻找增广路,找到增广路就沿着增广路增广。
    而如果没有找到增广路呢,那么就考虑所有现在在匈牙利树中的X节点(记为S集合),所有现在在匈牙利树中的Y节点(记为T集合),考察所有一段在S集合,一段在not T集合中的弧,取
    delta = min {l(xi)+l(yj)-w(xi,yj),xi ∈ S, yj ∈ not T}
    明显的,当我们把所有S集合中的l(xi)减少delta之后,一定会有至少一条属于(S,not T)的边进入相等子图,进而可以继续扩展匈牙利树,为了保证原来属于(S,T)的边不退出相等子图,把所有在T集合中的点的可行顶标增加delta。
    随后匈牙利树继续扩展,如果新加入匈牙利树的Y节点是未盖点,那么找到增广路,否则把该节点的对应的X匹配点加入匈牙利树继续尝试增广。
    复杂度分析:由于在不扩大匹配的情况下每次匈牙利树做如上调整之后至少增

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值