二分图最大匹配—Hopcroft-Karp算法

Hopcroft-Karp算法

详细算法讲解博客,推荐:https://blog.csdn.net/wall_f/article/details/8248373

基本算法

该算法主要是对匈牙利算法的优化,在寻找增广路径的时候同时寻找多条不相交的增广路径,形成极大增广路径集,然后对极大增广路径集进行推广。在寻找增广路径集的每个阶段,找到的增广路径集都具有相同的长度,且随着算法的进行,增广路径的长度不断地扩大。可以证明,最多增广 n0.5 次就可以得到最大匹配。

const int MAXN=510;// 最大点数
const int INF=1<<28;// 距离初始值
int bmap[MAXN][MAXN];//二分图
int cx[MAXN];//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy[MAXN]; //cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int nx,ny;
int dx[MAXN];
int dy[MAXN];
int dis;
bool bmask[MAXN];
//寻找 增广路径集
bool searchpath(){
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=nx;i++){
       //cx[i]表示左集合i顶点所匹配的右集合的顶点序号
       if(cx[i]==-1){
          //将未遍历的节点 入队 并初始化次节点距离为0
          Q.push(i);
          dx[i]=0;
       }
    }
    //广度搜索增广路径
    while(!Q.empty()){
       int u=Q.front();
       Q.pop();
       if(dx[u]>dis) break;
       //取右侧节点
       for(int v=1;v<=ny;v++){
          //右侧节点的增广路径的距离
          if(bmap[u][v]&&dy[v]==-1){
             dy[v]=dx[u]+1; //v对应的距离 为u对应距离加1
             if(cy[v]==-1) dis=dy[v];
             else{
                dx[cy[v]]=dy[v]+1;
                Q.push(cy[v]);
             }
          }
       }
    }
    return dis!=INF;
}

 //寻找路径 深度搜索
int findpath(int u){
    for(int v=1;v<=ny;v++){
       //如果该点没有被遍历过 并且距离为上一节点+1
       if(!bmask[v]&&bmap[u][v]&&dy[v]==dx[u]+1){
          //对该点染色
          bmask[v]=1;
          if(cy[v]!=-1&&dy[v]==dis){
             continue;
          }
          if(cy[v]==-1||findpath(cy[v])){
             cy[v]=u;cx[u]=v;
             return 1;
          }
       }
    }
    return 0;
 }

 //得到最大匹配的数目
int MaxMatch(){
    int res=0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    while(searchpath()){
       memset(bmask,0,sizeof(bmask));
       for(int i=1;i<=nx;i++){
          if(cx[i]==-1){
             res+=findpath(i);
          }
       }
    }
    return res;
}

【例题:HDU 2063 过山车】
http://acm.hdu.edu.cn/showproblem.php?pid=2063

Problem Description
RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?

Input
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。

Output
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。

Sample Input
6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0

Sample Output
3

可以直接匈牙利算法,这里只是看一下自己的板子对不对

//Hopcroft-Karp算法模板
const int maxn = 510;
const int inf = 0x3f3f3f3f3f;
int G[maxn][maxn];
int cx[maxn],cy[maxn];
int nx,ny;
int dx[maxn],dy[maxn];
int dis;
bool vis[maxn];
bool searchpath(){
    queue<int>q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=nx;i++){
       if(cx[i]==-1){
          q.push(i);
          dx[i]=0;
       }
    }
    while(!q.empty()){
       int u = q.front();
       q.pop();
       if(dx[u]>dis) break;
       for(int v=1;v<=ny;v++){
          if(G[u][v]&&dy[v]==-1){
             dy[v] = dx[u] + 1;
             if(cy[v]==-1) dis = dy[v];
             else{
                dx[cy[v]] = dy[v] + 1;
                q.push(cy[v]);
             }
          }
       }
    }
    return dis!=inf;
}
int findpath(int u){
    for(int v=1;v<=ny;v++){
       if(!vis[v]&&G[u][v]&&dy[v]==dx[u]+1){
          vis[v] = 1;
          if(cy[v]!=-1&&dy[v]==dis){
             continue;
          }
          if(cy[v]==-1||findpath(cy[v])){
             cy[v] = u;
             cx[u] = v;
             return 1;
          }
       }
    }
    return 0;
}
int MaxMatch(){
    int res=0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    while(searchpath()){
       memset(vis,0,sizeof(vis));
       for(int i=1;i<=nx;i++){
          if(cx[i]==-1){
             res += findpath(i);
          }
       }
    }
    return res;
}

AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<map>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 510;
const int inf = 0x3f3f3f3f3f;
int G[maxn][maxn];
int cx[maxn],cy[maxn];
int nx,ny;
int dx[maxn],dy[maxn];
int dis;
bool vis[maxn];
bool searchpath(){
    queue<int>q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=nx;i++){
       if(cx[i]==-1){
          q.push(i);
          dx[i]=0;
       }
    }
    while(!q.empty()){
       int u = q.front();
       q.pop();
       if(dx[u]>dis) break;
       for(int v=1;v<=ny;v++){
          if(G[u][v]&&dy[v]==-1){
             dy[v] = dx[u] + 1;
             if(cy[v]==-1) dis = dy[v];
             else{
                dx[cy[v]] = dy[v] + 1;
                q.push(cy[v]);
             }
          }
       }
    }
    return dis!=inf;
}
int findpath(int u){
    for(int v=1;v<=ny;v++){
       if(!vis[v]&&G[u][v]&&dy[v]==dx[u]+1){
          vis[v] = 1;
          if(cy[v]!=-1&&dy[v]==dis){
             continue;
          }
          if(cy[v]==-1||findpath(cy[v])){
             cy[v] = u;
             cx[u] = v;
             return 1;
          }
       }
    }
    return 0;
}
int MaxMatch(){
    int res=0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    while(searchpath()){
       memset(vis,0,sizeof(vis));
       for(int i=1;i<=nx;i++){
          if(cx[i]==-1){
             res += findpath(i);
          }
       }
    }
    return res;
}

int main(void){
    int num;
    while(scanf("%d",&num)&&num){
        memset(G,0,sizeof(G));
        scanf("%d%d",&nx,&ny);
        int u,v;
        for(int j=1;j<=num;j++){
             scanf("%d%d",&u,&v);
             G[u][v]=1;
        }
        printf("%d\n",MaxMatch());
    }
    return 0;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值