二分图的最大匹配

Maximum Matching on Bipartite Graph

这是一个经典中的经典问题。

求解这类问题,最常用的就是匈牙利算法,复杂度为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]) ;
                break ;
            }
        }
        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]) ;
                break ;

            }
        }
    }
    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种方法的时间界强不了多少。所以上面这些方法已经足够应付大部分的题目。

5、例题推荐:

Zju Online Judge 1137

Zju Online Judge 1140

Zju Online Judge 1525

Zju Online Judge 1882

Zju Online Judge 2039

Zju Online Judge 1626

Zju Online Judge 1516

Zju Online Judge 1568

Zju Online Judge 2636

Zju Online Judge 2333

Zju Online Judge 1059

Zju Online Judge 1197

Zju Online Judge 2429(本题是上面的第三种方法的应用,求最大流)

还有一些其它比赛的题目,比如NOI,CEOI等,不一一列举了。

以上是我对于二分图匹配的见解,欢迎大家提意见。

 

posted @ 2008-12-11 12:17  jesonpeng  阅读(436)  评论(0)    收藏  举报