算法学习-染色法判定二分图

二分图定义:
        如果一张无向图的 N 个节点可以分成 A,B 两个不相交的非空集合,并且同-集合内的点之间没有边相连,那么称该无向图为二分图。


定理:

二分图不存在奇环(长度为奇数的环),因为每一条边都是从一个集合走到另一个集合,只有走偶数次才可能回到同一个集合。


染色法
我们可以使用染色法来判定二分图。即尝试用两种颜色标记图中的节点,当一个点被标记后,所有与它相邻的节点应该标记与它相反的颜色,若标记过程产生冲突,则说明图中存在奇环。可以用 DFS 或 BFS 来实现。

例题(1):活动 - AcWing

题目:

给定一个 n 个点 m

条边的无向图,图中可能存在重边和自环。

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

输入格式

第一行包含两个整数 n 和 m。

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

输出格式

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

数据范围

1≤n,m≤10^5

输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5 + 10,M = 2e5 + 10;
int n,m;
int h[N],e[M],ne[M],idx;
int color[N];
bool st[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;
	for(int i = h[u];i != -1;i = ne[i]){
		int j = e[i];
		if(!color[j]){
			if(!dfs(j,3-c))
				return false;
		}else if(color[j] == c)
			return false;
	}
	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]){
			if(!dfs(i,1)){
				flag = false;
				break;
			}
		}
	}
	if(flag) puts("Yes");
	else puts("No");
	return 0;
}

例题(2):695. 劣马 - AcWing题库 

题目:

作为邪恶联盟的领导者,劣马有很多问题要处理。

联盟内有很多人关系恶劣甚至相互仇视,以至于劣马决定将联盟分成两个部门,以便将相互仇视的成员全部分开。

作为罪恶的领袖,劣马不会花费他宝贵的时间来作这项工作。

所以,他将这个任务交给了你。

输入格式

第一行包含整数 T,表示共有 T组测试数据。

每组数据第一行包含整数 M,表示共有 M对相互仇视的关系。

接下来 M行,每行包含两个用空格隔开的字符串,表示一对相互仇视的人员名单。

输出格式

每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 x 是组别编号(从 1 开始),如果能将所有相互仇视的成员全部分开,则 y 是 Yes,否则 y 是 No

数据范围

1≤T≤100,1≤M≤100
人员名字用字母和下划线构成,且区分大小写。在一组数据中,同一对仇视人员不会出现多次。
每对仇视关系涉及的二人一定不是同一个人。

输入样例:
2
1
Dead_Bowie Fake_Thomas_Jefferson
3
Dead_Bowie Fake_Thomas_Jefferson
Fake_Thomas_Jefferson Fury_Leika
Fury_Leika Dead_Bowie
输出样例:
Case #1: Yes
Case #2: No

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int t;
int color[N];
int e[N],ne[N],h[N],iii;
void add(int a,int b){
	e[iii] = b,ne[iii] = h[a],h[a] = iii ++;
}
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;
		}else if(color[j] == c)
			return false;
	}
	return true;
}
int main(){
	scanf("%d",&t);
	int num = 0;
	while(t --){
		num ++;
		int n;
		memset(color,0,sizeof color);
		memset(e,0,sizeof e);
		memset(ne,0,sizeof ne);
		memset(h,-1,sizeof h);
		scanf("%d",&n);
		map<string,int> a;
		int index = 0;
		iii = 0;
		for(int i = 0;i < n;i ++){
			string x,y;
			cin >> x >> y;
			if(!a.count(x))
				a[x] = index ++;
			if(!a.count(y))
				a[y] = index ++;
			add(a[x],a[y]);		
		}
		bool flag = true;
		for(int i = 0;i < index;i ++){
			if(!color[i]){
				if(!dfs(i,1)){
					flag = false;
					break;
				}
			}
		}
		if(flag) printf("Case #%d: Yes\n",num);
		else printf("Case #%d: No\n",num);
	}
	return 0;
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值