二分图匹配——匈牙利算法(时间复杂度O(nm))

什么是匈牙利算法

匈牙利算法是在两个集合中,各选出一个元素,若这两个元素情投意合,那么这两个元素就会脱离他们对应的集合,直到剩余集合中元素不能再组成配对为止(即在两个箱子里拿小球,如果这两个小球能消消乐,那么就拿到不能再消为止),匈牙利算法目的则是让匹配对数最多。

就像极度理性的男女分配一样,假如男1喜欢女1,男1也喜欢女2,但男2只喜欢女1,,那么在匹配的过程中,男2就找男1谈:“诶,我们都是兄弟,你看,我就只喜欢女1,喜欢其它人那不如让我狗带,你不也喜欢女2么,这不是两全其美。”(我纯爱战士表示狂怒,就和那个灵笼一样,所以这个ntr算法…【划掉】)

算法实现前的思路(一级结论)

1、匹配成功就要从集合中去除,但后面仍会拿来去匹配
2、两个匹配的元素只有一条无向边,所以从哪个集合先拿出小球开始匹配都可以
3、目的是尽可能多的匹配,所以当一个集合元素在另一个集合的元素都已经完成匹配时,要看看自己能不能ntr一下。
4、要考虑一个集合的每个元素n个,后面最坏情况要考虑每一条无向边m条,所以时间复杂度为O(nm)。

怎么用代码实现(二级结论)

1、因为只用看一个集合,所以,可以用数组来模拟单向指针(只要开一个数组match[],用这个数组存储前一个集合的下标1~n,这样就可以看成指向前一个集合的指针,来表示匹配了哪个元素)

2、可以用递归(函数套用来实现),find(x)函数功能是找到x这个元素的伴侣,而find(find(y))则是去找已经找到伴侣的能不能换一个。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=503,M=1e5+3;
int n1,n2,m;
int match[N];		//储存前一个集合的下标
int st[N];			//在一次寻找中,一个女的只考虑一次,防止重复搜索
int e[M],ne[M],idx,h[N];
void add(int a ,int b)				//链表存储
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool find(int x)
{
    for(int i=h[x];i!=-1;i=ne[i])					//找一下自己喜欢的女孩有谁
    {
        int j=e[i];
        if(!st[j])
        {
            st[j]=true;
            if(match[j]==0||find(match[j]))//match[j]==0表示那个女生没有男朋友,find(~)表示可以ntr
            {
                match[j]=x;				//赋值表示地址存放 ,指针指向前一个集合
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int res=0;
    memset(h,-1,sizeof h);
    cin>>n1>>n2>>m;
    while (m -- )
    {
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    for(int i=1;i<=n1;i++)
    {
        memset(st,false,sizeof st);				//每一次都要初始化
        if(find(i))res++;						//如果匹配成功,result++
    }
    cout<<res<<endl;
}


  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值