Bipartite Graph | Judging Bipartite Graph by Coloring Method

binary picture

A bipartite graph, also called a bipartite graph, divides all points into two sets, so that all edges only appear between points between sets, and there are no edges between points inside sets.

When a graph has only n vertices and no edges, it can only be divided into one set, which is also a bipartite graph

A bipartite graph, if and only if there are no odd rings in the graph. Therefore, as long as there are no rings or only even-numbered rings in the graph, it must be a bipartite graph.
It follows that the tree must be a bipartite graph .

A bipartite graph is not necessarily a connected graph, but may also be a non-connected graph.

Staining method

Define an array int color[N],, specify one of three states for each vertex, 0 means that it has not yet begun to dye, 1 means dyed in one color, and 2 means dyed in another color, thus dividing all vertices into two sets: 1 and 2.

1. From which starting point should we start dyeing?

If it is a connected graph, it can be colored from any vertex, because all vertices can be traversed from any starting point;
if it is a non-connected graph, at least one of the vertices of each connected subgraph must be taken out.

For the sake of convenience, each point is used as a starting point. When starting dyeing with this point as the starting point, first judge whether the point has been dyed, and if it is not dyed, start dyeing.

In this case, at most there are multiple cycles, and the effective number of cycle coloring depends on the number of connected graphs;
when the entire graph of N points is connected, it will only be colored once, and the remaining N-1 times are all It's an invalid loop.

2. What color is dyed at the starting point

Random dyeing at the starting point.
The sets of different colorings between non-connected graphs can be arranged and combined, so any coloring at the starting point can be selected.

3. How to dye?

Assuming that the starting point is dyed as color 1, then all nodes in the next layer are dyed as color 2, and then the next layer is color 1... and so on.

If there is no ring, it ends here;
if there is a ring (i,k1,k2,...,kn,i):

  1. For odd-numbered rings, the starting point i is dyed as color 1, and the last position kn is dyed as color 1, then its next position is the starting point to be dyed as color 2, contradictory, dyeing failure, not a bipartite graph.
  2. For even-numbered rings, the starting point i is dyed as color 1, and the last position kn is dyed as color 2, then its next position, the starting point, should be dyed as dye 1. No contradiction, the dyeing of the ring ends.

The problems of heavy edges and self-loops can be automatically filtered out by judging whether the nodes to be dyed have been dyed.
Therefore, heavy edges and self-loops can be stored in the graph and have no effect.

time complexity

O(m+n)

BFS staining

Therefore, each element in the queue needs to record two pieces of information, 1. node number; 2. dyed color.

1 represents a color set, and 2 represents a color set .
When the i-th layer is color 1, the i+1 layer is color 2 (3-1); when the i-th layer is color 2, the i+1 layer is Color 1 (3-2), so it can be operated uniformly:
that is, when the i-th layer is color x, layer i+1 is color 3-x

#include <iostream>
#include <cstring>

using namespace std;

#define ff first
#define ss second
typedef pair<int,int> PII; //first表示节点编号,second表示该结点染成的颜色
const int N=1e5+10,M=2e5+10;  "无向图,边要存储双倍"
PII q[N]; "图中的点不会重复加入队列,所以tt最多到N"
int hh,tt=-1;
int h[N],e[M],ne[M],idx; //无向无权图
int clr[N];  //记录每个点的染色,0代表未染色,1 2分别代表两种不同的颜色,clr数组兼顾了vis数组的作用,全局变量,默认为0
int n,m;

void add(int a,int b)//a->b
{
    
    
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool bfs(int vv,int cc)
{
    
    
    //先初始化,起点vv染色为cc
    clr[vv]=cc;  
    q[++tt]={
    
    vv,cc};
    
    while (hh<=tt) {
    
     //当队列中的点没有可达边或都染过色后,队列一直出队列,为空
        PII t=q[hh++];
        int v=t.ff,c=t.ss;
        //获取当前节点的编号v和颜色c,开始对它相邻的顶点染色
        for (int i=h[v];~i;i=ne[i]) {
    
     "~i等价于i!=-1,-1的二进制补码全1,只有-1取反是全0,此时停止,其他情况下~i都不是0"
            int j=e[i];   "v->j的边      不同于!i,所有的非零数!i都是0 "                            
            if (!clr[j])  clr[j]=3-c,q[++tt]={
    
    j,clr[j]}; //没有染色的情况
            else if (clr[j]==c)  return false;  //存在奇数环,矛盾,染色失败
            //还有一种情况,已经染色且clr[j]==3-c,说明存在偶数环,不用染色了,可以跳过,处理其它相邻的点。
        }   //从顶点v出发有很多出边,除了构成回路外,还有其它的点j
    }
    return true;  "因队列为空而退出,说明一个连通图中全部染色完成。"
                  "因为我们不知道一个连通图中有多少个顶点,所以必须等队列为空退出才行,中途退出则说明染色失败"
}

int main()
{
    
    
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    int a,b;
    while (m--) {
    
    
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    //判断是否是二分图
    bool tag;
    for (int i=1;i<=n;i++) {
    
    //防止出现一个图不是连通图的情况,确保每个点都可以染色,结点从1开始编号
        if (!clr[i]) {
    
    //每一次bfs就是给一个连通图染色,每一个连通图都从1号颜色开始染色
            tag=bfs(i,1); //染色,并返回是否染色成功;起点i染色为颜色1
            if (tag==false) break;  //染色失败就退出
        }
    }
    tag==true?puts("Yes"):puts("No");
    
    return 0;
}

DFS stain

#include <iostream>
#include <cstring>

using namespace std;

const int N=1e5+10,M=2e5+10;
int h[N],e[M],ne[M],idx;
int clr[N];
int n,m;

void add(int a,int b)
{
    
    
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}


bool dfs(int v,int c)//欲将结点为v的顶点染色为c
{
    
                "从上到下染色,然后回溯返回是否染色成功"
    if (clr[v]==c) return true;   //偶数环,该路径染色结束,成功
    else if (clr[v]==3-c)  return false;  //奇数环,该路径染色结束,失败
    else {
    
                               //该点没染色的情况
        clr[v]=c;
        for (int i=h[v];~i;i=ne[i]) {
    
      //给下一层的每个点都进行染色
            if (dfs(e[i],3-c)==false)  return false;  //如果中途发现染色失败就返回
        }      "调用函数的同时使用返回值"
        return true;  //染色成功
    }
}


int main()
{
    
    
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    int a,b;
    while (m--) {
    
    
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    bool tag;
    for (int i=1;i<=n;i++) {
    
    
        if (!clr[i]) {
    
    
            tag=dfs(i,1);
            if (tag==false) break;
        }
    }
    tag==true?puts("Yes"):puts("No");
    
    return 0;
}

dfs can be written in many ways. The above mentioned each time when the recursion enters, it is necessary to judge whether the colored points are reasonable;
it can also be considered that the points to be colored each time the recursion is entered are reasonably colored points.

bool dfs(int u,int c)
{
    
    
    clr[u]=c;
    for (int i = h[u]; i != -1; i = ne[i]) {
    
    
        int j=e[i];
        if (!clr[j]) {
    
       //1 2分别代表两种染色,u染了1,它下一个j就染2;染了2,j就染1
            if (dfs(j, 3 - c) == false) return false;  //综上,u染c,j染3-c
        }
        else if (clr[j]==c)  return false;   //这也是最后一次递归结束的条件
    }    //当前要染色3-c,但是如果已经染色c的话,说明存在奇数环,不是二分图
    return true;
}

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/114086975