AcWing 860. 染色法判定二分图

题目传送门

一、二分图的概念

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

举个栗子吧:
2.png

这是不是二分图?
反正我第一次看觉得不是。其实,是的,他是二分图,尽管看上去是连着的。
若我们将图中的一些边转一下,变成:
3.png
这就是一个明显的二分图。集合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;
}
原文地址:https://www.cnblogs.com/littlehb/p/15337776.html