图论进阶——二分图染色

二分图定义

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

下面放上一张图:

前面的讲解可能有一些不太好懂,这里给出一个简洁易懂的解释:

对于任意一个连通图,每个点可以被染成 0 0 1 1 两种颜色。

如果存在一种染色方案,使被任意一条边连接的两个点 ( u , v ) (u,v) 颜色不同,那么这个图就是一个二分图。

如上图所示,所有边的两个端点的颜色都是不相同的。

并且二分图真正的样子就是一个普通图,上图只是依据颜色将其分为了两个集合。

请记住,二分图是一个无向图。

一个小定理

当且仅当无向图G的回路个数为偶数时,图G为一个二分图。

无回路的图也是二分图。

二分图染色

有上面给出的定义可知,当你找到一个连通图中的一个点 n n 时,其周围通过一条边连接的点所可以染的颜色是唯一确定的。故对于一个联通的无向图来说,如果它是一个二分图,那么其染色方式必定只有 2 2 种(因为对于一个点 n n ,其染色方式有且仅有 2 2 种)。

如果不存在一个二分图,那么当你重复上面的染色过程是,必定会遇到矛盾,判定矛盾的方式也很简单,如果存在一条边 ( u , v ) (u,v) ,使得当你搜到 v v u u 已经被染成了与 v v 相同的颜色,那么该图不为二分图,直接退出循环。

实现方式

如果该图不连通,请按如下操作对于各个部分分别实现。

for(int i=1;i<=n;i++){
    if(!col[i])  bfs(i);
}

初始:将点 1 1 入队

s t e p   1 : step\space 1: 选取队首点 v v ,然后将其出队。

s t e p   2 : step\space 2: 枚举与其通过一条边连接的所有点,如果未染色,则染上与 v v 相反的颜色,将其入队。如果 u u 已染色,那么判断是否相同,相同则无影响,不同则二分图不存在。

s t e p   3 : step\space 3: 重复进行步骤 1 1 直到队列空。

Code

#include<bits/stdc++.h>
using namespace std;
int first[1000005],nxt[1000005],to[1000005],tot=0;
int col[100005];
void Add(int x,int y){
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
bool bfs(int x){
	queue<int> q;
	col[x]=0;
	q.push(x);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int e=first[u];e;e=nxt[e]){
			int v=to[e];
			if(col[v]==-1){
				col[v]=col[u]^1;
				q.push(v);
				continue;
			}
			if(col[v]==col[u])  return false;
		}
	}
	return true;
}
int main(){
	memset(col,0xff,sizeof(col));
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		Add(x,y);
		Add(y,x);
	}
	for(int i=1;i<=n;i++){
		if(col[i]==-1){
			if(!bfs(i)){
				cout<<"NO\n";
				return 0;
			}
		}
	}
	cout<<"YES\n";
	return 0;
}
发布了45 篇原创文章 · 获赞 18 · 访问量 4757

猜你喜欢

转载自blog.csdn.net/wljoi/article/details/101385813
今日推荐