Hopcroft-Karp算法

任务:给定一个二分图,用Hopcroft-Karp算法求这个二分图的最大匹配数。
说明:
dx[/]dy[/] d x [ / ] , d y [ / ] 分别表示二分图左右部顶点的距离标号;
mx[/]my[/] m x [ / ] , m y [ / ] 分别表示二分图左右部顶点的匹配节点;
Hopcroft相比普通的匈牙利算法来说,由于每次是增广一系列路径,所以更快。我们每次从所有未匹配的左部节点开始 BFS B F S ,进行距离标号。对于每一个队列中的左部节点 X X ,考虑与他相邻的所有右部节点Y:如果 Y Y 是一个未匹配的右部节点,则说明至少还存在一条增广路,用一个bool变量 flag f l a g 记录,以便之后增广;否则,将 Y Y 的匹配节点加入到队列中,顺便求出距离标号。当BFS结束时,若不存在增广路(即 flag f l a g false f a l s e ),那么算法结束;否则对于每一个没有匹配的左部节点 X X 执行匈牙利算法的find(X)操作,在这里, find(X) f i n d ( X ) 过程中,只考虑这样的边 uv ( u , v ) :满足 dx[u]+1=dy[v] d x [ u ] + 1 = d y [ v ]

模板:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
using namespace std;
#define ll long long

const int maxn = 50000;


int n1, n2;
vector<int>g[maxn + 10];  //实现邻接表;g[i]表示与左边点i相连的右边的点;
int mx[maxn + 10], my[maxn + 10];
queue<int> que;
int dx[maxn + 10], dy[maxn + 10];
bool vis[maxn + 10];

bool find(int u)
{
        for(int i = 0; i < g[u].size(); i++)
        {
            if(!vis[g[u][i]] && dy[g[u][i]] == dx[u] + 1)
            {
                vis[g[u][i]] = true;
                if(!my[g[u][i]] || find(my[g[u][i]])) {
                    mx[u] = g[u][i];
                    my[g[u][i]] = u;
                    return true;
                }
            }
        }
    return false;
}

int matching(){
    memset(mx, 0, sizeof(mx));
    memset(my, 0, sizeof(my));
    int ans = 0;
    while(true)
    {
        bool falg = false;
        while(!que.empty()) que.pop();
        memset(dx, 0, sizeof(dx));
        memset(dy, 0, sizeof(dy));
        for(int i = 1; i <= n1; i++)
            if(!mx[i]) que.push(i);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int i = 0; i < g[u].size(); i++)
            {
                if(!dy[g[u][i]]){
                    dy[g[u][i]] = dx[u] + 1;
                    if(my[g[u][i]]) {
                        dx[my[g[u][i]]] = dy[g[u][i]] + 1;
                        que.push(my[g[u][i]]);
                    }
                    else {
                        flag = true;
                    }
                }
            }
        }
        if(!flag) break;
        memset(vis, 0,sizeof(vis));
        for(int i = 1; i <= n1; i++)
            if(!mx[i] && find(i)) ans++;
    }
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值