二分图

104 阅读2分钟

给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环。

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

输入格式

第一行包含两个整数 nn 和 mm

接下来 mm 行,每行包含两个整数 uu 和 vv,表示点 uu 和点 vv 之间存在一条边。

输出格式

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

数据范围

1n,m1051≤n,m≤10^5

输入样例:

4 4
1 3
1 4
2 3
2 4

输出样例:

Yes

题目分析

首先了解一下二分图的概念:

给一个无向图,若能将图的所有节点分为两类,每一类间的节点互不连通即没有连边,则这样的图称为二分图。

探究一下二分图的本质,即无向图中不能存在节点数为奇数的环,手动模拟一下,假设环上的每个节点按左右左右这样的分边方式,若环上节点的数量为奇数则存在最后一个点与第一个点相连且同边,即当前图并非二分图。同样的方式进行模拟,对于偶数环和节点链和单独节点则不会产生冲突。

接下来我们按以上思路来进行解题,本题以 dfsdfs 方式进行分析。首先我们依次枚举所有节点,每个节点的初始状态为未赋值,我们先将其赋值为 11,然后依次枚举其相邻边并将未赋值的节点赋值为 22,并依次向深层 dfsdfs,假设父节点赋值为 uu,则子节点赋值为 3u3-u 即可。在枚举的过程中若出现已经被赋值的节点需要重新被赋新值则意味着当前节点位于一个奇数环上,即这个图并不是二分图;反之当枚举全部节点后仍未出现错误则当前图为二分图。

每个节点会被枚举一次,每条边会被枚举一次,最终时间复杂的为 O(n+m)O(n + m)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
vector<int> g[N];
int f[N];
int n, m;

bool dfs(int x, int u)
{
    f[x] = u;
    for (auto y : g[x])
    {
        if (f[y] == u || !f[y] && !dfs(y, 3 - u)) return false;
    }
    return true;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n >> m;
    while (m --)
    {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    
    bool flg = true;
    for (int i = 1; i <= n; i ++)
        if (!f[i] && !dfs(i, 1)) 
        {
            flg = false;
            break;
        }
    
    if (flg) cout << "Yes";
    else cout << "No";
    return 0;
}