二分图匹配

相关定义

  • 匹配

在图论中,一个匹配是一个边集,其中任意两条边都没有公共顶点。

  • 最大匹配

一个图的所有匹配中元素最多的匹配就是最大匹配

  • 完美匹配

若一个图中的某个匹配中任意一个顶点都是匹配点(即是一条匹配边的端点),则称这个匹配是这个图的完美匹配。

  • 二分图

若一个图的顶点能被分成 \(X\)\(Y\) 两个集合且任意两个位于同一集合内部的点之间没有边相连,那称这个图为可二分的。

现阶段(NOIP)只关注二分图的匹配问题。一般图的匹配是NOI+。

匈牙利算法

求二分图的最大匹配,现在常用的就是费用流和匈牙利

再次给出一些定义:

  • 交替路

从一个未匹配点出发,依次经过“非匹配边 — 匹配边 — 非匹配边 — \(\cdots\)”形成的路径叫做交替路。

  • 增广路

从一个未匹配点出发,依次经过“非匹配边 — 匹配边 — 非匹配边 — \(\cdots\) — 非匹配边”形成的路径叫做增广路。(其实就是最后经过边是未匹配边的交替路)

由定义易得到,每次找到一条增广路,将边取反(匹配边变为非匹配边,非匹配边变为匹配边,可行性证明略手模一下就能理解),就可以使匹配边数量 \(+1\)

这就是匈牙利算法的基本原理

假设我们已经找到了一个匹配,想要求一个更大的匹配。

我们可以试着考虑从一个未匹配点开始DFS,若找到一个增广路,就代表还有更大匹配。

若增广失败,则此时的匹配为最大匹配。

code:

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

const int N=510,M=1e5+10;

int n1,n2,m;
int head[N],nxt[M],ver[M],tot=0;
void add(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int match[N];	//每一个左部点对应的右部点 
bool vis[N];	//标记 

bool find(int x)
{
	for(int i=head[x];i;i=nxt[i])
	{
		int y=ver[i];
		if(!vis[y]) 
		{
			vis[y]=1;
			if(match[y]==0||find(match[y]))		//若未匹配or重匹配成功 
			{
				match[y]=x;		//更新匹配 
				return 1; 
			}
		}
	}
	return 0;
}

int main()
{
	scanf("%d%d%d",&n1,&n2,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);	//虽然是无向图,但是我们只需要建从左到右的点 
	}
	
	int res=0;
	for(int i=1;i<=n1;i++)
	{
		memset(vis,0,sizeof vis);	//清空标记数组
		if(find(i)) res++;//如果成功找到增广,答案加一 
	}
	printf("%d",res);
	return 0;
}
posted @ 2020-11-01 14:59  RemilaScarlet  阅读(156)  评论(0)    收藏  举报