在解决判定和匹配任务之前,我想我们应该先了解什么是二分图
定义
二分图,又称二部图,英文名叫 Bipartite graph。
二分图是什么?节点由两个集合组成,且两个集合内部没有边的图。
换言之,存在一种方案,将节点划分成满足以上性质的两个集合。 − ( O I W i k i ) -(OI\ Wiki) −(OI Wiki)
那么首先我们先看一个一般的图
上图用了 6 6 6个点, 6 6 6条边。这样看起来没什么特别的对吧,我们将他做一下调整。
此图和上边的是等价的,但是我们将他们分成了两个集合 A , B A,B A,B,并且每个集合内部都没有边相连,这种图就是二分图。
了解了什么是二分图,我想我们可以开始我们要讲的判定了,现在你可以动手画一画,是不是所有的图都是二分图呢?显然这是不可能的,但这其中又有什么关系吗?比如现在我给出这样一个图👇
你能将他分成二分图吗?
当我们做这件事的时候,会发现无论怎么分,点 5 5 5必然会和 0 0 0或 1 1 1在一个集合中,为什么?因为图中存在奇数环,这时候我们得出了第一个结论: 当图中有奇数环时,他一定不是二分图 , 反之没有奇数环的图一定是二分图 当图中有奇数环时,他一定不是二分图,反之没有奇数环的图一定是二分图 当图中有奇数环时,他一定不是二分图,反之没有奇数环的图一定是二分图
我们多举一个例子:
图中存在长度为 4 和 8 4和8 4和8的环,我们现在将他变成二分图
这是非常容易办到的(
接下来我们介绍如何去判定一个图是不是二分图。我这里给出的是一个常用的算法 染色法判断二分图 染色法判断二分图 染色法判断二分图
原理:我们可以将两个集合中的点给一个颜色,例如: A A A集合中的点我们都染成红色, B B B集合中的点我们都染成黑色,我们开一个 c o l o r [ ] color[\ ] color[ ]数组来记录每个点的颜色,起始,我们遍历每个点,如果没有被染色,我们就将他染成红色,然后将与他直接相连的点全部染成黑色,同理当我们将这个点染成黑色后,我们就要将与他直接相连的所有点染成红色,在这期间如果染色失败,那么就不是二分图,什么时候会出现染色失败呢?当我们染完一个点后,在染他的所有相邻的点的时候发现有一个点的颜色和他相同,也就是下面图中的情况.
1 1 1号点发现他的相邻点 0 0 0号点也是红色,那么这样就不行了对吧,就算我们把 1 1 1号点染成黑色,那么也会发现他的相邻点 5 5 5也是黑色,这时候就只能宣布:染色失败,图不是二分图
例题:染色法判定二分图
给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环。
请你判断这个图是否是二分图。
输入格式
第一行包含两个整数 n n n 和 m m m。
接下来 m m m 行,每行包含两个整数 u u u 和 v v v,表示点 u u u 和点 v v v 之间存在一条边。
输出格式
如果给定图是二分图,则输出 Y e s Yes Yes,否则输出 N o No No。
数据范围
1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
这个题是染色算法的板子题,下面给出完整代码:
#include <bits/stdc++.h>
using namespace std;
//------邻接表存边
const int N=1e5+10,M=2*N;
int h[N],e[M],ne[M],idx;
int color[N];
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//-------
//-------染色法判断二分图
bool dfs(int u,int c)
{
color[u]=c;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!color[j])//相邻点没有颜色,就染成不同的颜色
{
if(!dfs(j,3-c))return false;
}else if(color[j]==color[u])return false;//相邻点与本身的颜色相同
}
return true;//将相邻点全部染色成功,只能说明这个点与他的相邻点没有产生矛盾
}
//--------
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
//----读入无向边
memset(h,-1,sizeof h);
int n,m;cin>>n>>m;
while(m--)
{
int a,b;cin>>a>>b;
add(a,b);add(b,a);
}
//-----
bool f=true;
for(int i=1;i<=n;i++)//开始染色
{
if(!color[i])
{
if(!dfs(i,1))f=false;//如果有一个点染色失败,就宣布失败
}
}
if(f)cout<<"Yes";else cout<<"No";
return 0;
}
到这里,我相信你已经对如何判定一个图是不是二分图有所了解了,那么这是远远不够的,因为二分博大精深(
我们将学习下一个知识点:二分图的最大匹配
这里我们不引入所谓的增广路概念,他对我来说比较抽象 h h hh hh,所以我们直接用白话的意思翻译一下,什么是最大匹配
转载知乎@青烟
他的最大匹配数就是 3 3 3,因为有三对点完成了匹配,分别是: { x 1 , y 4 } , { x 2 , y 2 } , { x 3 , y 3 } \{x_1,y_4\},\{x_2,y_2\},\{x_3,y_3\} { x1,y4},{ x2,y2},{ x3,y3}
所以最大匹配数就是一个二分图中,从两个集合中各拿出一个点,组成一对的最大对数。
了解了概念,我们如何求呢?这里给大家介绍一种算法:匈牙利算法
我们用一个例题来说明:二分图的最大匹配
给定一个二分图,其中左半部包含 n 1 n_1 n1 个点(编号 1 ∼ n 1 1∼n_1