匈牙利算法+题目:二分图的最大匹配

289 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情

概念

匹配成功 :不存在两条边共用两个点 匈牙利算法返回匹配成功的最大数量是多少

例子

image.png 男女之间的恋爱关系,不存在脚踩两条船的情况

基本思路

不撞南墙不回头:每次匹配的时候,如果匹配到的点已经属于其他人了,那么就看一下那个"其他人"有没有备胎,能不能换一个

时间复杂度

最坏情况:O(n * m) ,n个男生,m个女生 但是很多少时候都会比这个小

题目

www.acwing.com/problem/con… image.png

代码

#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

const int N = 510, M = 100010; // 虽然是无向边,每次只需要存左边指向右边的点就行了,因为只会找左边的点指向右边的哪个点

int n1, n2, m;
int h[N], e[M], ne[M], idx; // 邻接表
int match[N]; // 右边的点对应的点,就是哪个妹子跟哪个男生在一块
bool st[N]; // 判重,每次不要重复搜一个点

// 插入一条a指向b的边
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]; // 用j来表示当前点的编号
        if (!st[j]) // 如果妹子之前没有被考虑过
        {
            st[j] = true;
            if (match[j] == 0 || find(match[j])) // 如果妹子没有匹配过男生的话match == 0表示没有匹配男生,或者说虽然匹配了男生,但是可以为男生找到下家
            {
                match[j] = x; // 给妹子匹配男生
                return true; 
            }
        }
    }
    return false;
}

int main()
{
    scanf("%d%d%d", &n1, &n2, &m);
    
    memset(h, -1, sizeof h);	 
    
    while (m--)
    {
        int a, b; 	 
        scanf("%d%d", &a, &b);
        add(a, b); 
    }
    
    int res = 0; // 存的是匹配的数量
    // 依次分析一下哪个男生应该匹配哪个女生
    for (int i = 1; i <= n1; i++)
    {
        memset(st, false, sizeof st); // 每次分析前,将所有妹子清空,表示这些妹子还没考虑过
    	if (find(i)) res++; // 如果成功匹配到的话,结果res++
    }
    
    printf("%d\n", res);
    
    return 0;
}