染色法判定二分图 竟然这么简单

说明:优快云和公众号文章同步发布,需要第一时间收到最新内容,请关注公众号【比特正传】。

0、概念解释

二分图:将图中n个顶点分为两个互不相交的子集A和B,每条边所关联的两个顶点u和v分别在两个不同的子集中,这样的图可以称为二分图。

二分图不存在奇数环(充分必要条件)

1、问题描述

背景:
我发现洛谷上竟然没有二分图判定的模板题,这很不利于初学者的学习,因此我做了一点微薄的贡献,提供了一道判定二分图的模板题,并配置了20个测试用例。

题目链接:
https://www.luogu.com.cn/problem/U425878

图片

2、染色法算法描述

染色法,顾名思义,就是给每个节点染色,由于是二分图,因此我们有两种颜色,比如颜色1和颜色2,通过dfs或者bfs搜索的时候,给每个节点进行染色,如果染色过程中,出现冲突,比如一条边相连的两个节点u和v都是同一个颜色,那么说明不是二分图,返回false即可,如果搜索完后,每个节点都有了颜色,并且没有出现冲突,那么说明就是二分图。

如下图是一个二分图

图片

如下图不是一个二分图

上图无论如何染色,总有两个相邻节点是同一个颜色,因此不是二分图。

3、AC code

下面分别通过dfs和bfs来实现染色法判定二分图,可以结合代码及注释进行理解。

DFS搜索


#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int n, m, u, v; 
vector<int> g[N];
int color[N];
// x为当前节点,c为当前颜色,取值为 1 或 2 
bool dfs(int x, int c) {
  color[x] = c;    // 染色 
  for(int ne : g[x]) {  // 然后搜索x节点的相邻节点进行染色 
    if(!color[ne]) {  // 没有染过色,那就染色并搜索 
      if(!dfs(ne, 3-c)) return false;  // 3-c可以将1变为2,2变为1 
    } else if(color[ne] == color[x]) return false;  // 已经染过色了,但是和x是一个色,出现冲突了,返回false 
  }
  return true;  // 全程没有出现冲突,那么返回true 
}
int main() {
  cin >> n >> m;
  for(int i=1; i<=m; i++) {
    cin >> u >> v;
    g[u].push_back(v);  // 无向图 
    g[v].push_back(u);
  } 
  bool flag = true;  // 标记是否为二分图 
  for(int i=1; i<=n; i++) {  // 因为有可能是不连通图,所以每个连通块都需要搜索一次 
    if(color[i]) continue;  // i所在的联通块已经染过色了,就跳过 
    if(!dfs(i, 1)) {  // 如果染色过程中返回了false,那说明出现了矛盾,就不是二分图 
      flag = false;  // 标记为不是二分图 
      break;  // 可以结束循环了 
    }
  }
  if(!flag) cout << "No";
  else cout << "Yes";
  return 0;
}

BFS搜索


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int n, m, u, v; 
queue<int>q;
vector<int>g[maxn];
int color[maxn];
bool bfs(int x)
{
  q.push(x);  //先放入当前点 
  color[x]=1;   //上色 
  while(!q.empty()){
    int v=q.front(); q.pop();
    for(int ne : g[v]) {
      if(color[ne] == 0) {
        color[ne] = 3-color[v];//没有标记过就标记上与v相反的颜色
        q.push(ne);
      }else if(color[v] == color[ne]) return false;
    }
  }
  return true;
}
int main() {
  cin >> n >> m;
  for(int i=1; i<=m; i++) {
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  } 
  bool flag = true;  // 标记是否为二分图 
  for(int i=1; i<=n; i++) {  // 因为有可能是不连通图,所以每个连通块都需要搜索一次 
    if(color[i]) continue;  // i所在的联通块已经染过色了,就跳过 
    if(!bfs(i)) {  // 如果染色过程中返回了false,那说明出现了矛盾,就不是二分图 
      flag = false;  // 标记为不是二分图 
      break;  // 可以结束循环了 
    }
  }
  if(flag) cout << "Yes";
  else cout << "No";
  return 0;
}

动手做起来吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值