【图论】浅析匈牙利算法

本文不对匈牙利算法的证明作解释。

预备知识

  • 二分图: 设 \(G=(V,E)\) 是一个无向图,如果顶点 \(V\) 可分割为两个互不相交的子集 \((A,B)\) ,并且图中的每条边 \((i,j)\) 所关联的两个顶点 \(i\)\(j\) 分别属于这两个不同的顶点集 \((i \in A,j \in B)\),则称图 \(G\) 为一个二分图。
  • 匹配:满足其中任意两条边都没有公共顶点的边集
  • 最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。

简介

匈牙利算法用于求二分图的最大匹配
模板题传送门:https://www.acwing.com/problem/content/description/863/

步骤

我们拿经典的男女配对来解释一下流程。

情境:给出 \(n_1\) 个男生, \(n_2\) 个女生,他们如果之间连边则代表着他们之间相互喜欢(不考虑同性恋(故保证是二分图))。

左边是男生,右边是女生。
一号男生和一、二号女生相互喜欢,二号男生和二、三号女生相互喜欢,三号男生和三号女生相互喜欢。

对于一号男生:
他和二号女生互相喜欢(假设他在找女友的时候优先遍历到二号女生),而二号女生现在没有男友,配对成功,返回。

对于二号男生:
他和三号女生互相喜欢(假设他在找女友的时候优先遍历到三号女生),而三号女生现在没有男友,配对成功,返回。

目前配对状况:

对于三号男生:
他和三号女生互相喜欢,但是此时三号女生有男友(二号)了,所以他标记了一下三号女生,然后去找到二号男生问他你能不能再去找一个其他女友?),然后二号男生就去遍历他的女友,发现三号已经被标记了,但二号没有标记,但是此时二号女生有男友(一号)了,所以二号男生就标记了二号女生,类似于三号男生的行为,然后去找到一号男生问他你能不能再去找一个其他女友,一号男生也开始遍历自己的女友,最后发现一号女生没有标记,于是配对成功。

代码实现:
我们规定:

vis[i]=true 表示 i 号女生被标记,否则没有
match[i]=j 表示 i 号女生的男友是 j 号,如果没有男友则默认是 0

完整代码:

#include<bits/stdc++.h>
using namespace std;

const int N=505;
int n1,n2,m;

int h[N],tot;
struct node{
	int to,next;
}e[N*N];
void add(int u,int v){e[tot].to=v, e[tot].next=h[u], h[u]=tot++;}

int match[N];
bool vis[N];

bool find(int x){
	for(int i=h[x];~i;i=e[i].next){
		int go=e[i].to;
		if(!vis[go]){
			vis[go]=true;
			if(!match[go] || find(match[go])){
				match[go]=x;
				return true;
			}
		}
	}
	return false;
}

int main(){
	memset(h,-1,sizeof h);
	cin>>n1>>n2>>m;
	while(m--){
		int u,v; cin>>u>>v;
		add(u,v);
	}
	
	int res=0;
	for(int i=1;i<=n1;i++){
		memset(vis,false,sizeof vis);
		if(find(i)) res++;
	}
	cout<<res<<endl;
	
	return 0;
}
posted @ 2021-03-22 12:04  HinanawiTenshi  阅读(101)  评论(0编辑  收藏  举报