​题目传送门​

860. 染色法判定二分图_ios

一、二分图的概念

二分图,是图论中的一种特殊模型,设\(G=(V,E)\)是一个无向图,如果顶点\(V\)可分割为两个互不相交的子集\((A,B)\),并且同一集合中不同的两点没有边相连。

这就是二分图。

举个栗子吧:

860. 染色法判定二分图_无向图_02

这是不是二分图?

反正我第一次看觉得不是。其实,是的,他是二分图,尽管看上去是连着的。

若我们将图中的一些边转一下,变成:

860. 染色法判定二分图_邻接表_03

这就是一个明显的二分图。集合A与B中的点互不相连!!!

只要两个点之间有边,那么这两个点就不能同属一个集合,必须分在两边。

染色法是判断二分图的最好办法:

二、DFS实现

#include <bits/stdc++.h>

using namespace std;

//二分图
const int N = 100010; //M比给定的要多一倍

int n, m;
int h[N], e[N << 1], ne[N << 1], idx;
int color[N];

//邻接表保存图
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

//u:点 c:颜色值
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; //这个3-c用的太妙了
} else if (color[j] == c) return false; //染色冲突,则表示失败
}
return true;
}


int main() {
//读入优化
ios::sync_with_stdio(false);
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]) {
//把当前点染色为1
if (!dfs(i, 1)) {
flag = false;
break;
}
}
//是二分图
if (flag) puts("Yes");
else puts("No"); //不是二分图
return 0;
}


三、BFS实现

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;
typedef pair<int, int> PII;

int e[N << 1], ne[N << 1], h[N], idx;
int n, m;
int color[N];

void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool bfs(int u) {
//假设 1:黑,2:白,这样方便理解一些
color[u] = 1;

//手写队列
int hh = 0, tt = 0;
PII q[N]; //两个属性:节点号,颜色
q[0] = {u, 1};//将节点u入队列,颜色为黑

while (hh <= tt) {
PII t = q[hh++];
int ver = t.first, c = t.second;
//找到这个节点关联的其它节点
for (int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
//没染色就染成相反的颜色
if (!color[j]) {
color[j] = 3 - c;
//并且把这个新的节点入队列,再探索其它的相邻节点
q[++tt] = {j, 3 - c};
} else if (color[j] == c) return false;
//染过的,有两种情况,一种是与本次要求的染色一样,一种是不一样,
//不一样就是矛盾
}
}
return true;
}

int main() {
//读入优化
ios::sync_with_stdio(false);
cin >> n >> m;
//初始化邻接表
memset(h, -1, sizeof h);

while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a); // 无向图,两个方向
}

int flag = true; //一旦发现异常,立即需要报告!
//遍历每一个节点进行尝试染色
for (int i = 1; i <= n; i++) {
//如果还没有进行染色
if (!color[i]) {
//染它!并且看看是不是成功完成了染它及其扩展操作
if (!bfs(i)) {
flag = false; //发现异常就不用继续了!
break;
}
}
}
if (flag) puts("Yes");
else puts("No");
return 0;
}