二分图匹配Hopcroft-Carp算法介绍

我们在做二分图匹配的时候,最喜欢选择的就是匈牙利算法,但是我们可以知道匈牙利算法的复杂度是O(n*e),那么如果对于一个点和边比较多的图,匈牙利算法很容易超时,所以我们采用Hopcroft-Carp算法来解决这个问题,这个算法能够在 O(sqrt(n)*e)的复杂度内实现二分图匹配。
下面我就来讲一讲这个算法的操作过程。
简单来说,这个算法就是在匈牙利算法的基础上,先通过BFS找到多条不相交最短增广路,并且在这个基础上再往下增广,所以同时可以增广多条路,降低了时间复杂度。
那么我们是怎么实现的呢?
1、我们先将每一个要匹配的点通过BFS找到一个对应最短增广路,形成一个增广路集合
2、对于我们找到增广路集合,我们去匹配这个集合中的每个增广路,(我们用之前的允许弧的方法去记录上面找到的最短不交叉的最短增广路)将他往下延伸。
这样我们再用匈牙利算法去匹配。
算法分析,匈牙利算法是通过对每一个点去增广匹配,现在我们是通过记录一个d数组,实现对每一条路进行增广匹配,同时间增广的效率变高了,所以时间复杂度就降低了。
先贴一份模板:

const int inf=0x3f3f3f3f;
int Mx[maxn];
int My[maxn];
int dx[maxn];
int dy[maxn];
bool used[maxn];
int dis,Ny,Nx;
bool searchP()  
{  
    dis=INF;  
    int i,v,u;  
    std::queue<int> Q;  

    memset(dx,-1,sizeof(dx)); 
     //d数组里存放的是增广路到达该点的距离,dx是左边的点,dy是右边的点
    memset(dy,-1,sizeof(dy));  
    for(i=0;i<Nx;i++)  
    {  
        if(Mx[i]==-1)  
        {  //遇到没有匹配的点,就开始寻找他的增广路
            Q.push(i);  
            dx[i]=0;  
        }  
    }  
    while(!Q.empty())  
    {  
        u=Q.front();  
        Q.pop();  
        if(dx[u]>dis) break;  
        for(v=0;v<Ny;v++)  
        {  
            if(g[u][v]&&dy[v]==-1)  
            {  
                dy[v]=dx[u]+1;  
                if(My[v]==-1) dis=dy[v];  
                else  
                {  //如果这个点已经被匹配了,这条路不是增广路,那么我们就要继续往下找,直到找到一条增广路为止
                    dx[My[v]]=dy[v]+1;  
                    Q.push(My[v]);  
                }  
            }  
        }  
    }  
    return dis!=INF;  
}  

bool DFS(int u)  
{  
    int v;  
    for(v=0;v<Ny;v++)  
    {  
        if(g[u][v]&&!used[v]&&dy[v]==dx[u]+1)  //现在我们找到一条边,并且这个点刚好是我们找增广路时候的下一个点,说明这两个点有匹配的需要
        {  
            used[v]=true;  
            if(My[v]!=-1&&dy[v]==dis) continue;  
            //此时说明已经找到最后一个点了,它并不是一个增广路,就放弃这条路
            if(My[v]==-1||DFS(My[v]))  
            {  
                My[v]=u;  
                Mx[u]=v;  
                return true;  
            }  
        }  
    }  
    return false;  
}  
 //这里就和匈牙利算法没有什么区别了
int Hungary()  
{  
    int u;  
    int ret=0;  
    memset(Mx,-1,sizeof(Mx));  
    memset(My,-1,sizeof(My));  
    while(searchP())  
    {  
        memset(used,false,sizeof(used));  
        for(u=0;u<Nx;u++)  
           if(Mx[u]==-1&&DFS(u))  ret++;  
    }  
    return ret;  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值