浅谈二分图
定义
二分图,又称二部图,英文名叫 Bipartite graph。
二分图是什么?节点由两个集合组成,且两个集合内部没有边的图。
换言之,存在一种方案,将节点划分成满足以上性质的两个集合。
性质
- 如果两个集合中的点分别染成黑色和白色,可以发现二分图中的每一条边都一定是连接一个黑色点和一个白色点。
- 二分图不存在长度为奇数的环,因为每条边的 \(u\) 在一个集合, \(v\) 在另一个集合,所以想要从一个点出发,再回到这个点只可能经过偶数条边。
应用
二分图最大匹配
增广路算法 (Augmenting Path Algorithm)
假设有 \(n\) 个顶点,\(m\) 条边。
因为增广路长度为奇数,路径起始点非左即右,所以我们先考虑从左边的未匹配点找增广路。 注意到因为交错路的关系,增广路上的第奇数条边都是非匹配边,第偶数条边都是匹配边,于是左到右都是非匹配边,右到左都是匹配边。 于是我们给二分图 定向,问题转换成,有向图中从给定起点找一条简单路径走到某个未匹配点,此问题等价给定起始点 \(s\) 能否走到终点 $t $ 。 那么只要从起始点开始 DFS 遍历直到找到某个未匹配点,\(O(m)\)。未找到增广路时,我们拓展的路也称为 交错树。
代码
#include <bits/stdc++.h>
using namespace std;
int n,m,t,book[505],match[505];
vector <int> e[505];
int dfs(int x,int tag)
{
if(book[x]==tag)return 0;
book[x]=tag;
for(auto v : e[x])
{
if((match[v]==0)||dfs(match[v],tag))//这个人还没有选或者让与它连边的点换一个点连边
{
match[v]=x;
return 1;//匹配成功
}
}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
for(int i=1;i<=t;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(dfs(i,i))
{
sum++;
}
}
cout<<sum;
return 0;
}
二分图最小点覆盖(König 定理)
最小点覆盖:选最少的点,满足每条边至少有一个端点被选。
二分图中,最小点覆盖 \(=\) 最大匹配。
二分图最大独立集
最大独立集:选最多的点,满足两两之间没有边相连。
因为在最小点覆盖中,任意一条边都被至少选了一个顶点,所以对于其点集的补集,任意一条边都被至多选了一个顶点,所以不存在边连接两个点集中的点,且该点集最大。因此二分图中,最大独立集 \(=\) \(n-\)最小点覆盖。