相关定义
-
匹配
在图论中,一个匹配是一个边集,其中任意两条边都没有公共顶点。
-
最大匹配
一个图的所有匹配中元素最多的匹配就是最大匹配
-
完美匹配
若一个图中的某个匹配中任意一个顶点都是匹配点(即是一条匹配边的端点),则称这个匹配是这个图的完美匹配。
-
二分图
若一个图的顶点能被分成 (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;
}