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;
}
posted @ 2020-08-23 11:21  Hayasaka  阅读(87)  评论(0编辑  收藏  举报