ACwing 860 - 染色法判断二分图(染色法)
给定一个n个点m条边的无向图,图中可能存在重边和自环。
请你判断这个图是否是二分图。
输入格式
第一行包含两个整数n和m。
接下来m行,每行包含两个整数u和v,表示点u和点v之间存在一条边。
输出格式
如果给定图是二分图,则输出“Yes”,否则输出“No”。
数据范围
1≤ n,m≤ 105
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
题目大意:
输入n m 表示n 个顶点 m 条边,可能有重边和自环。判断该图是不是一个二分图。
解题思路:
判断一个图是否是二分图,判断是否存在奇数环即可,用染色法判断即可。
二分图是值可以将点集分成两半,每个集合内都没有边,但可以与另一个集合中的点构成边,当且仅当一个图不存在奇数环的时候是二分图,奇数环是指环中的边数为奇数,染色法:将一个图染成只有1和2两种颜色,dfs每一个点,连通的染成不同颜色,如果发现这两个点连通且颜色相同,则说明出现了奇数环,一定不是一个二分图。
举个例子:一个图
- 1 2
- 2 3
- 3 4
- 4 5
- 5 1
这是一个存在奇数环的图,假设将节点1染成1,那么2就要染成2,3染成1,4染成2,5染成1,但是5 1 连通,本来应该将1染成2,但1已经是1了,很显然这不是一个二分图。
Code:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 50;
int h[N], e[2 * N], ne[2 * N], idx = 0;
int color[N];
int n, m;
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;//将该点染成c色,这里c只有1 2 两种情况。
for (int i = h[u]; ~i; i = ne[i])//邻接表遍历边集
{
int j = e[i];
if (!color[j])//如果没被染过则dfs j点并判断
{
if (!dfs(j, 3 - c)) return false;
}
else if (color[j] == c) return false;//如果j被染过且颜色相同,那么3 - c 一定和j的颜色不同,但j已经被染过了,不构成二分图。
}
return true;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m--)
{
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])//如果没有被染过则dfs该点,将这个连通块全部染色并判断
{
if (!dfs(i, 1))
{
flag = false;
break;
}
}
puts(flag ? "Yes" : "No");
return 0;
}