数据结构与算法学习笔记----染色法判定二分图

发布于:2025-02-11 ⋅ 阅读:(45) ⋅ 点赞:(0)

数据结构与算法学习笔记----染色法判定二分图

@@ author: 明月清了个风
@@ first publish time: 2024.12.21

ps⭐️其实就是个DFS,注释很详细,可以看一下注释

二分图

二分图的特点是:图中的顶点集可以被分成两个不相交的子集,并且图中的每一条边都连接这两个子集中的一个顶点。换句话说,图的所有边都仅连接两个子集中的顶点,两个子集内部的顶点之间没有边,所有的边都用于连接分别在两个集合中的两点。

没有奇数环:一个图是二分图当且仅当图中不存在奇数环。

证明:

假设有一张图 G G G中包含一个回路 C C C,其长度为奇数,设其长度为 2 k + 1 2k+1 2k+1,那么回路中有 2 k + 1 2k+1 2k+1个顶点。

从任一节点为起点开始染色:第一个顶点染色为 1 1 1,第二个顶点染色为 2 2 2。。。。。,以此类推,直到回路的最后一个顶点。因为回路的长度为 2 k + 1 2k+1 2k+1,所以经过 2 k + 1 2k+1 2k+1步回到起点,最终起点会被重新染色为 2 2 2,与开始时矛盾,因此一张图是二分图,不可能含有长度为奇数的回路。

染色法

所谓的染色法其实就是一个图的遍历问题,在遍历的过程中对每个顶点的颜色进行记录就可以了,需要注意的就是检查冲突。

这里我们使用的是DFS遍历,当然BFS也可以。

Acwing 860. 染色法判定二分图

[原题链接](860. 染色法判定二分图 - AcWing题库)

给定一个 n n n个点 m m m条边的有向图,图中可能存在重边和自环

请你判断这个图是否是二分图

输入格式

第一行包含三个整数 n n n, m m m

接下来 m m m行,每行包含两个个整数 u u u v v v,表示存在一条从点 u u u到点 v v v的有向边

输出格式

如果给定图是二分图,则输出 Yes,否则输出 No

数据范围

1 ≤ n , m ≤ 1 0 5 1 \leq n,m \leq 10^5 1n,m105,

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010, M = N * 2;

int n, m;
int h[N], e[M], ne[M], idx;   // 根据数据范围可以看出是稀疏图,用邻接表存
int color[N];    // 用于记录每个点的染色状态

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 != -1; i = ne[i])  // 遍历该点所在的连通块
    {
        int j = e[i];
        if(!color[j])  // 连通的点没有染色就染色
        {
            if(!dfs(j, 3 - c)) return false;  // 染色失败返回false
        }
        else if(color[j] == c) return false;   // 染色冲突返回false
    }
    
    return true;
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);     // 初始化表头
    
    for(int i = 0; i < m; i ++)   // 读入所有边
    {
        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(!dfs(i, 1))  // 判断是否染色成功
            {
                flag = false;  //失败将flag置0
                break;
            }
        }
    }
    
    if(flag) puts("Yes");
    else puts("No");
    
    return 0;
}