您的位置:首页 > 其它

匈牙利算法求二分图的最大匹配寻找增广路的几种思路(转)

2009-07-22 18:11 309 查看
这是一个经典中的经典问题。
求解这类问题,最常用的就是匈牙利算法,复杂度为O(n^3)。
我在这里详细的介绍三种不同的实现,针对不同的题目,他们有不同的效果呦^_^。
先定义一些框架:
int nx , ny , g[maxn][maxn] , cx[maxn] , cy[maxn] ;
// cx[i]表示与Xi匹配的Y顶点
// cy[i]同理

这三种方法不同的地方就在于不同的寻找增广路径的方法。

1、DFS增广
从某一个X顶点出发,用深度优先的策略寻找增广路,
并在找到之后,巧妙地利用递归来修改匹配。
int mk[maxn] ;
int path(int u){
for (int v = 0 ; v < ny ; v++)
if (g[u][v] && !mk[v]){
mk[v] = 1 ;
if (cy[v] == -1 || path(cy[v])) {
cx[u] = v ;
cy[v] = u ;
return 1 ;
}
} return 0 ;
}

int MaxMatch()
{
int res(0) ;
memset(cx , 0xff , sizeof(cx)) ;
memset(cy , 0xff , sizeof(cy)) ;
for (int i = 0 ; i <= nx ; i++)
if (cx[i] == -1){
memset(mk , 0 , sizeof(mk));
res += path(i) ;
}
return res ;
}
优点:实现简洁,理解容易
适用:稠密图,由于边多,dfs找增广路很快
复杂度:O(n^3)

2、BFS增广
int pred[maxn] , mk[maxn] , open[maxn] ;
int MaxMatch()
{
int i , u , v , t , d , e , cur , tail , res(0) ;
memset(mk , 0xff , sizeof(mk)) ;
memset(cx , 0xff , sizeof(cx)) ;
memset(cy , 0xff , sizeof(cy)) ;
for (i = 0 ; i < nx ; i++){
pred[i] = -1 ;
for (open[cur = tail = 0] = i ; cur <= tail && cx[i] == -1 ; cur++){
for (u = open[cur] , v = 0 ; v < ny && cx[i] == -1 ; v ++) if (g[u][v] && mk[v] != i)
{
mk[v] = i ; open[++tail] = cy[v] ; if (open[tail] >= 0) { pred[open[tail]] = u ; continue ; }
for (d = u , e = v ; d != -1 ; t = cx[d] , cx[d] = e , cy[e] = d , e = t , d = pred[d]) ;
}
}
if (cx[i] != -1) res++ ;
}
return res ;
}
适用:稀疏二分图,边少,增广路短
复杂度:O(n^3)

3、多增广路
这是一类方法,每次增广同时寻找多条不相交增广路,由Hopcroft和Karp于1973年提出,有非常优秀的时间界。
以下代码由BFS的框架直接修改而来:

int pred[maxn] , mk[maxn] , open[maxn] , src[maxn] ;
int MaxMatch(){
int i , u , v , t , d , e , cur , tail , res(0) , p , flag ;
memset(mk , 0xff , sizeof(mk)) ;
memset(cx , 0xff , sizeof(cx)) ;
memset(cy , 0xff , sizeof(cy)) ;
for (p = 1 , flag = 1 ; flag ; p++){
flag = 0 ;
for (cur = tail = 0 , i = 0 ; i < nx ; i ++)
if (cx[i] == -1)open[++tail] = i , pred[i] = -1 , src[i] = i ;
for ( ; cur <= tail ; cur++){
u = open[cur] ; if (cx[src[u]] != -1) continue ;
for (v = 0 ; v < ny ; v ++) if (g[u][v] && mk[v] != p){
mk[v] = p ; open[++tail] = cy[v] ;
if (open[tail] >= 0) {
pred[open[tail]] = u ;
src[open[tail]] = src[u] ;
continue ;
}
for (tail-- , flag = 1 , d = u , e = v ; d != -1 ; t = cx[d] , cx[d] = e , cy[e] = d , e = t , d = pred[d]) ;
}
}
}
for (i = 0 ; i < n ; i++) res += (cx[i] != -1) ;
return res ;
}

还可以用DFS来找多增广路。
根据图的稠密程度,来考虑用哪种实现。
优点:复杂度低,实现相对简单
复杂度:O(n^2.5)
应用:这种方法还可以应用到类似二分图的网络中,或者多部的网络中,求最大流。可以极高的加快程序的运行。

4、总结
目前关于二分图的最大匹配的最快算法是一种特殊的网络流算法,Even和Tarjan于1975提出的单位容量最大流算法。其复杂度为O(m*sqrt(n))。但是这种方法来求最大匹配实现麻烦,需要用两种不同的算法组合,而且比上面的第3种方法的时间界强不了多少。所以上面这些方法已经足够应付大部分的题目。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: