二分图就是把一个集合内的点分到两个集合中,两个集合之间有边相连.各个集合内部无边:
判断一个图是否是是二分图可以通过一个性质去判断:二分图一定不包含奇数环
什么是奇数环?
所谓的奇数环就是指一个图中存在奇数条边:
为什么说二分图一定不包含奇数环?
证明:
因为 二分图是集合内部无边,而每两个点之间又都有边相连,因此可以得知一条边的两段节点一定是属于不同的集合.
如下图所示,该图中包含奇数环.假设第一个节点是属于左边集合的,那么第二个节点就属于右边集合,第三个节点是属于左边集合的,然后又回到第一个节点,此时又该轮到右边集合了.一个节点不可能即属于左边集合又属于右边集合.
我们再添加一个节点,让其变为偶数环,此时我们就发现每个节点当且仅当只属于一个集合:
因此,包含奇数环的一定不是二分图,二分图中一定不包含奇数环.
证毕
我们怎么去判断一个图是不是二分图?
通过上面的结论,我们可以通过判断一个图中是否包含奇数环来判断这个图是否是二分图.
我们判断一个图中是否包含奇数环的方法就是通过染色法去判断.
我们染第一个节点(称为a节点)为1,那么下个节点就染为2,如果最后染回a节点的时候变为染2了,那说明该图包含奇数环,不是二分图.
否则就说明该图不包含奇数环,是二分图.
code
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1e5+10,M=2e5+10;
int h[N],e[M],ne[M],color[N];
int idx;
void add(int a,int b)
{
e[idx]=b; //当前节点的出边的节点值为b
ne[idx]=h[a]; //指向下一个节点
h[a]=idx++; //跳到下一个节点
}
bool dfs(int u,int c)
{
color[u]=c; //当前节点染成1
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]==c) return false; //如果当前节点和上一个节点的颜色一样,那就违背二分图的性质了
}
return true; //染色无矛盾,返回true
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h); //初始化头节点
for(int i=0;i<m;i++)
{
int a,b;cin>>a>>b;
add(a,b),add(b,a);
}
bool flag=true;
for(int i=1;i<=n;i++)
{
if(!color[i]) //如果当前节点没有被染色,那就染一下色
{
if(!dfs(i,1)) //如果染色有矛盾的话
{
flag=false;
break;
}
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}