染色法
- 将所有点分成两个集合,使得所有边只出现在集合之间,就是
二分图
- 二分图:一定不含有奇数环,可能包含长度为偶数的环, 不一定是连通图
dfs版本
- 代码思路:
- 染色可以使用
1
和2
区分不同颜色,用0
表示未染色
- 遍历所有点,每次将未染色的点进行
dfs
, 默认染成1
或者2
- 由于某个点染色成功不代表整个图就是二分图,
因此只有某个点染色失败才能立刻break/return
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10; // 由于是无向图, 顶点数最大是N,那么边数M最大是顶点数的2倍
int e[M], ne[M], h[N], idx;
int st[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs(int u, int color) {
st[u] = color;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]) {
if(!dfs(j, 3 - color)) return false;
}else if(st[j] == color) return false;
}
return true;
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m --){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b,a); // 无向图,a->b, b->a
}
bool flag = true;
for(int i = 1; i <= n; i ++){
if(!st[i]){
if(!dfs(i, 1)){
flag = false;
break;
}
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
bfs版本
- 代码思路
- 颜色
1
和 2
表示不同颜色, 0
表示 未染色
- 定义
queue
是存PII
,表示 <点编号, 颜色>,
- 同理,遍历所有点, 将未染色的点都进行bfs
- 队列初始化将第
i
个点入队, 默认颜色可以是1
或2
while
(队列不空)
- 每次获取队头
t
, 并遍历队头t
的所有邻边
- 若邻边的点未染色则染上与队头
t
相反的颜色,并添加到队列
- 若邻边的点已经染色且与队头
t
的颜色相同, 则返回false
C++ 代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10;
typedef pair<int, int> PII;
int e[M], ne[M], h[N], idx;
int n, m;
int st[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool bfs(int u){
int hh = 0, tt = 0;
PII q[N];
q[0] = {u, 1};
st[u] = 1;
while(hh <= tt){
auto t = q[hh ++];
int ver = t.first, c = t.second;
for (int i = h[ver]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j])
{
st[j] = 3 - c;
q[++ tt] = {j, 3 - c};
}
else if(st[j] == c) return false;
}
}
return true;
}
int main(){
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while(m --){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
int flag = true;
for(int i = 1; i <= n; i ++) {
if (!st[i]){
if(!bfs(i)){
flag = false;
break;
}
}
}
if (flag) puts("Yes");
else puts("No");
return 0;
}
https://blog.csdn.net/WJPnb1/article/details/126332263?spm=1001.2014.3001.5501 第一次写csdn博客,写了这个找二分图最大匹配的,大家可以来看看,有问题交流交流
貌似开始把未染色值全初始化为0,一边染上色为任意值c,另一边则染成相反值-c更有利于新手理解,也不要来回想进一步是1还是2咯,hhh。
xdm,你们都用哪个版本啊,哪个版本更好啊
想知道为什么大佬的BFS用的好像是栈?是我判断错了吗?
不是用的队列吗,你说的是哪个
不好意思是我错了,我看到变量名就想成栈顶
hh
for(int i = 1; i <= n; i ++){
if(!st[i]){
if(!dfs(i, 1)){
flag = false;
break;
}
}
}
不是从1开始深搜就可以全部染色了吗?为什么还要遍历所有节点啊?
因为图 不一定联通,非连通图也可以 是 二分图
原来如此,懂了,阿里嘎多!
哦谢谢!
哪位大佬可以帮帮我,谢谢
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
using namespace std;
const int N = 100010;
typedef pair[HTML_REMOVED] PII;
int idx = 0, e[N], ne[N], h[N], color[N], n, m;
PII q[N * 2];
void add(int a, int b)
{
e[idx] = a, ne[idx]= h[a], h[a] = idx ++;
}
bool bfs(int u)
{
color[u] = 1;
q[0] = {u, 1};
int tt = 0, hh = 0;
while(hh <= tt)
{
auto t = q[hh ++];
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
while(m –)
{
int a, b;
cin >> a >> b;
if(a != b)
{
add(a, b);
add(b, a);
}
}
}
自环就奇数环 就是false ;重2次边 涂2次 第一次涂上 第二次查看颜色对不对
orz
能帮我看一下哪错了吗,超时了。
这里题目给的是无向图,如果有N条边,这需要2N长的数组来存;改为e[2N],ne[2N], 就好了
感谢大佬回复hh,我也更新一下题解,在代码注释里强调这个。
#include<bits/stdc++.h>
用这个的话,会增加不少的运行时间,其他倒还好。不过我有点好奇,
int color[N]={0};
和 直接int color[N];
有区别么?为什么要赋值{0}
呢?如果在intmian上面写直接默认0把,这样是多余的
所以如果队列要存pair类型的元素的话,只能手动模拟嘛qwq
可以调用容器0.0,可能是因为队列手动模拟比较简单,像红黑树,哈希表,这些我就没见过人硬写了(狗头)
也是可以用标准库里的
queue
,不过大家也是学基础课下来,在bfs
就顺便可以练习一下手写队列hh。需要改
2
处地方吧(下面附上完整代码):1. 加一下
include
2. 然后改一下
bfs
函数十分感谢💞
牛逼 感谢
求大佬解答一下这句话: 无向图, 所以最大边数是2倍
因为是无向图是特殊的有向图 每一个点之间都互相连接着到彼此的两条边 所以最大是2倍
自己写的时候,就是这里忘了 T.T
大佬 bfs的时候 为什么要for啊,这样不是很多次bfs了吗? 直接一次bfs(1) 然后while(不空) 不行吗
7 7
1 2
2 3
1 3
4 5
5 6
6 7
7 4
画图发现两个环,一次搜索遍历不了所有点
哦哦
写的很好
赞一个~
谢谢hh
大佬,请问能帮忙看下我这个bfs代码哪里有问题吗?感觉思路和你的差不多,我用的s数组存储颜色,起始都是-1,染色1或0。部分正确。。。
##### 有
2
个问题1.
add
函数没写对,复习一下AcWing 826. 单链表,应该改成这样2.
bfs
的模板也没对,模板是每次取队头,然后遍历队头所有的邻边,你这样写每次只会遍历了x
这个点的邻边,根本不能遍历完整个图。。我直接在你的代码基础上改(另外bfs的写法最好紧跟y总基础课 AcWing 844. 走迷宫的模板会清晰很多)。
然后你直接把我上面改好的
add
函数和bfs
函数替换之后就可以AC
了~~哦哦懂了,谢谢大佬!十分感谢!!!
不客气,互相学习hh
不对吧,add函数也可以这样写
哈喽,可以说一下是哪里不对吗?
其实只有第二个是改对了的
我是想知道,哪里不对,以及为什么不对,不太懂你意思。。
add可以写成
学死了
噢噢是可以,idx可以放前面++。上面确实评论错了。谢谢指出。
染色失败相当于至少存在2个 (相邻点)染了相同的颜色
好滴!已修正。严谨。
码风舒服
哈哈,是闫式风格