染色法判断二分图(Bipartite Graph Coloring)

染色法判断二分图

时间复杂度 O(n+m)

题一 一个图当且仅当可以被2-染色,那么它是二分图
或者说
二分图当且仅当图中不含奇数环(奇数环指环的边数是奇数)
简单来说:相邻的节点一定不属于一个集合

由于图中不含有奇数环,所以我们可以通过DFS或BFS来对每个节点进行遍历染色,染色过程中也一定不会有矛盾
核心伪代码:

for(int i = 1;i<=n;i++)
{
    if(i未被染色)
        dfs(i,1);     //利用深度优先遍历吧i所在的连通块整个染成1号颜色
}

#include <iostream>
#include <cstring>

const int N = 100010;
int h[N], e[N*2], next[N*2],color[N], idx, n, m;	//color分 0 —— 未染色,1 —— 红色,2 —— 黑色
void add(int a, int b)
{
	e[idx] = b, next[idx] = h[a], h[a] = idx++;
}
bool dfs(int index,int cls)
{
	color[index] = cls;	//点index染成cls色

	
	for(int i =h[index];i!=-1;i=next[i])	//遍历和index相邻的点
	{
		int j = e[i];
		if(!color[j])	//如果相邻的点没有染色,我们将其染成另一个颜色,同时检查相邻的点的相邻的点
		{
			if(!dfs(j,3-cls)) return false;
		}
		else if (color[j] == cls) return false;		//如果染色,检查是否重色,重色则不是二分图
	}
	return true;	//如果遍历完图后没有出现矛盾,则说明是二分图
}

int main()
{
	std::memset(h, -1, sizeof h);
	std::cin >> n >> m;

	while(m--)		//无向图,需要赋两次相向边
	{
		int a, b;
		std::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) std::cout << "Yes";
	else std::cout << "No";
}