二分图判定
概述
若一个无向图的结点可以分为不相交的两个部分使得每条边的两个端点分别在不同的部分,则称这样的无向图为二分图(bipartite graph)
算法:黑白二着色(bicoloring)
扫描所有结点,从未访问过的结点开始,将其为染色邻接点染为不同的染色。若已染色的邻接点与当前结点颜色相同,则该图不是二分图;反之该图是二分图
理解
扫描所有结点,从未访问过的结点开始
无向图不一定连通!故需要扫描所有结点进行DFS或BFS。
此外显然二分图是可合并的,任意无向图是二分图当且仅当其各个连通分量都是二分图(若均为二分图,则每个二分图的两个子集可以合并;否则无法找到两个集合满足每条边的两端点分别在两个集合)
若已染色的邻接点与当前结点颜色相同,则该图不是二分图;反之该图是二分图
从这个图可以很清楚的看出,由二分图的定义可知,任意一个点通过一条边只能走到另一个集合中的点,一条边的两个端点的颜色一定不同。上述算法的正确性是显然的
模板
小tip:这里我把颜色定为1和2,可以用异或3代替3-
但注意选择的颜色不能和数组的初始化值相同!否则无法判定是否已被染色
DFS可能会栈溢出,所以这里给出BFS版代码
1 #include<cstdio> 2 #include<cctype> 3 using namespace std; 4 #define re register int 5 int read() 6 { 7 int f=0,x=0; 8 char c=getchar(); 9 while(!isdigit(c)) 10 { 11 f=f|c=='-'; 12 c=getchar(); 13 } 14 while(isdigit(c)) 15 { 16 x=(x<<1)+(x<<3)+(c^48); 17 c=getchar(); 18 } 19 return x; 20 } 21 struct edge 22 { 23 int to,pre; 24 }edges[1000001]; 25 int head[1000001],tot; 26 int n,m; 27 int queue[1000001],left=1,right=0; 28 int color[1000001];//表示每个点的颜色(1为白,2为黑) 29 bool bipartite() 30 { 31 for(int i=1;i<=n;i++) 32 if(!color[i]) 33 { 34 queue[++right]=i; 35 color[i]=1; 36 while(right>=left) 37 { 38 int x=queue[left++]; 39 for(int j=head[x];j;j=edges[j].pre) 40 if(color[edges[j].to]==color[x]) 41 return false; 42 else if(!color[edges[j].to]) 43 { 44 color[edges[j].to]=3^color[x]; 45 queue[++right]=edges[j].to; 46 } 47 } 48 } 49 return true; 50 } 51 void add(int x,int y)//邻接表存边 52 { 53 edges[++tot].to=y; 54 edges[tot].pre=head[x]; 55 head[x]=tot; 56 } 57 int main() 58 { 59 scanf("%d%d",&n,&m); 60 for(int i=1;i<=m;i++) 61 { 62 int x,y; 63 scanf("%d%d",&x,&y); 64 add(x,y),add(y,x); 65 } 66 printf("%d\n",bipartite()); 67 return 0; 68 }