Gráfico bipartito | Juzgando el gráfico bipartito por método de coloración

imagen binaria

Un gráfico bipartito, también llamado gráfico bipartito, divide todos los puntos en dos conjuntos, de modo que todos los bordes solo aparecen entre puntos entre conjuntos y no hay bordes entre puntos dentro de conjuntos.

Cuando un gráfico tiene solo n vértices y no tiene aristas, solo se puede dividir en un conjunto, que también es un gráfico bipartito

Un gráfico bipartito, si y solo si no hay anillos impares en el gráfico. Por lo tanto, siempre que no haya anillos o solo anillos pares en el gráfico, debe ser un gráfico bipartito.
De ello se deduce que el árbol debe ser un gráfico bipartito .

Un gráfico bipartito no es necesariamente un gráfico conectado, pero también puede ser un gráfico no conectado.

Método de tinción

Defina una matriz int color[N], especifique uno de los tres estados para cada vértice, 0 significa que aún no ha comenzado a teñirse, 1 significa teñido en un color y 2 significa teñido en otro color, dividiendo así todos los vértices en dos conjuntos: 1 y 2.

1. ¿Desde qué punto de partida debemos empezar a teñir?

Si es un grafo conectado, se puede colorear desde cualquier vértice, porque todos los vértices se pueden atravesar por cualquier punto de partida;
si es un grafo no conectado, se debe sacar al menos uno de los vértices de cada subgrafo conectado.

Por conveniencia, cada punto se utiliza como punto de partida. Al comenzar a teñir con este punto como punto de partida, primero juzgue si el punto ha sido teñido, y si no está teñido, comience a teñir.

En este caso, el número de ciclos es como máximo varias veces, y el número efectivo de colores de ciclo depende del número de gráficos conectados;
cuando el gráfico completo de N puntos está conectado, solo se coloreará una vez y los N restantes -1 veces es todo. Es un bucle inválido.

2. De qué color se tiñe en el punto de partida

Teñido aleatorio en el punto de partida.
Los conjuntos de diferentes colores entre gráficos no conectados se pueden organizar y combinar, por lo que se puede seleccionar cualquier color en el punto de partida.

3. ¿Cómo teñir?

Suponiendo que el punto de partida se tiñe como el color 1, todos los nodos de la siguiente capa se tiñen como el color 2, y luego la siguiente capa es el color 1 ... y así sucesivamente.

Si no hay anillo, termina aquí;
si hay anillo (i, k1, k2, ..., kn, i):

  1. Para los anillos impares, el punto de partida i se tiñe como color 1, y la última posición kn se tiñe como color 1, luego su siguiente posición es el punto de partida para teñirse como color 2, contradictorio, falla de teñido, no bipartito grafico.
  2. Anillo de número par, el punto de partida i se tiñe como color 1, y la última posición kn se tiñe como color 2, luego su siguiente posición es el punto de partida para teñir como tinte 1, sin contradicción, el teñido del anillo termina.

Los problemas de bordes pesados ​​y bucles automáticos se pueden filtrar automáticamente al juzgar si se han teñido los nudos que se van a teñir.
Por lo tanto, los bordes pesados ​​y los bucles automáticos se pueden almacenar en el gráfico y no tienen ningún efecto.

complejidad del tiempo

O (m + n)

Tinción BFS

Por lo tanto, cada elemento en la cola necesita registrar dos piezas de información, 1. número de nodo, 2. color teñido.

1 representa un conjunto de colores y 2 representa un conjunto de colores .
Cuando la i-ésima capa es el color 1, la capa i + 1 es el color 2 (3-1); cuando la i-ésima capa es el color 2, la i + 1 capa es el color 1 (3-2), por lo que se puede operar de manera uniforme:
es decir, cuando la i-ésima capa es el color x, la capa i + 1 es el 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;
}

Tinción DFS

#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 se puede escribir de muchas formas, lo anterior cada vez que ingresa recursivamente es necesario juzgar si el punto teñido es razonable,
también se puede considerar que los puntos a teñir cada vez que ingresa recursivamente son puntos teñidos razonablemente.

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;
}

Supongo que te gusta

Origin blog.csdn.net/HangHug_L/article/details/114086975
Recomendado
Clasificación