D24 二分图判定 染色法

D24 二分图判定 染色法_哔哩哔哩_bilibili

 

二分图 - OI Wiki

如果图 𝐺 =(𝑉,𝐸) 的顶点集 𝑉 可以分为两个互不相交的子集 𝑋 和 𝑌,使得每条边 𝑒 ∈𝐸 的两个端点都分别属于 𝑋 和 𝑌,就称图 𝐺 是一个 二分图(bipartite graph).集合 𝑋 和 𝑌 常称作它的两个 部分(part),或者分别称为二分图的左部和右部.当二分图的两个部分 𝑋 和 𝑌 已知时,也可以用三元组 (𝑋,𝑌,𝐸) 来表示二分图 𝐺.树、偶环、网格图等都是常见的二分图的例子.

性质

  • 图 𝐺 是可 2‑着色的.也就是说,可以用至多两种颜色给图的所有顶点染色,并且保证相邻顶点颜色不同.
  • 图 𝐺 中不存在奇环

image

可以考虑用两种颜色尝试给图 𝐺 染色.任选连通分量中的一个顶点 𝑠,进行 DFS,并记录连通分量中每个顶点 𝑣 与 𝑠 的距离.从 𝑠 开始,在 DFS 生成树上进行归纳可知,如果存在一种可行的染色方法,一定是根据每个顶点 𝑣 到起点 𝑠 的距离的奇偶性分别染成两种颜色.

继而考虑那些不在生成树中的边.如果这些非树边的两个端点的颜色都不一样,就说明当前的染色方案可行;否则,就不存在可行的方案.

进一步地,两个顶点颜色不同,当且仅当它们到树根 𝑠 的距离一奇一偶,这又等价于加入该非树边形成的是一个偶环而非奇环.因此,只要没有奇环,这些非树边必然连接颜色不同的点,进而整张图都可以用两种颜色染色,图就一定是二分图.

判定

判定一个图是不是二分图,尝试给二分图染色即可.可以使用 DFS 或者 BFS 来遍历这张图.如果发现了奇环,也就是出现无法染色的情况,那么就不是二分图;否则,就是二分图.

  • 遍历顶点,如果发现还没有染色的顶点,说明发现新的连通分量.
  • 任选一种颜色给该顶点染色,并以它为起点做 DFS 或者 BFS,尝试给该连通分量染色.
  • 如果发现尚未染色的顶点,将尚未染色的顶点染上与当前顶点相反的颜色.
  • 如果发现已经染色的顶点,检查颜色是否与当前顶点相同.相同,则不是二分图,直接返回;否则,继续遍历.

应用

很多图论优化问题都可以在二分图上高效解决.

  • 极大团(平凡)
  • 最小点着色(平凡)
  • 最小边着色
  • 最大匹配
  • 最小边覆盖
  • 最小点覆盖
  • 最大独立集
  • 最大权匹配
  • 二分图博弈

U248878 染色法判定二分图 - 洛谷

U169194 【模板】二分图判定 - 洛谷

// 染色法判定二分图 O(n+m)
#include<bits/stdc++.h>
using namespace std;

const int N=100010,M=2*N;
int n,m;
int h[N],to[M],ne[M],idx;
int color[N];

void add(int a,int b){
  to[++idx]=b; ne[idx]=h[a]; h[a]=idx;
}
bool dfs(int u,int c){
  color[u]=c;
  for(int i=h[u];i;i=ne[i]){
    int v=to[i];
    if(!color[v]){ //若v未染色
      if(dfs(v,3-c)) return 1; //继续搜索v,给v染不同颜色(1或2)
    }
    else if(color[v]==c) return 1; //若v已染色且与u同色,说明有奇环
  }
  return 0;
}
int main(){
  cin>>n>>m;
  for(int i=0,a,b;i<m;i++){
    cin>>a>>b;add(a,b);add(b,a);
  }
  for(int i=1;i<=n;i++)if(!color[i])if(dfs(i,1)){
    puts("No"); return 0;
  }
  puts("Yes"); return 0;
}

 

P1330 封锁阳光大学 - 洛谷

// 染色法判定二分图 O(n+m)
#include<bits/stdc++.h>
using namespace std;

const int N=100010,M=2*N;
int n,m;
int h[N],to[M],ne[M],idx;
int color[N];
int a,b,ans;

void add(int a,int b){
  to[++idx]=b; ne[idx]=h[a]; h[a]=idx;
}
bool dfs(int u,int c){
  color[u]=c;
  c==1?++a:++b; //统计两部点数
  for(int i=h[u];i;i=ne[i]){
    int v=to[i];
    if(!color[v]){ //若v未染色
      if(dfs(v,3-c)) return 1; //继续搜索v,给v染不同颜色(1或2)
    }
    else if(color[v]==c) return 1; //若v已染色且与u同色,说明有奇环
  }
  return 0;
}
int main(){
  cin>>n>>m;
  for(int i=0;i<m;i++){
    cin>>a>>b;add(a,b);add(b,a);
  }
  for(int i=1;i<=n;i++)if(!color[i]){
    a=b=0;
    if(dfs(i,1)){
      puts("Impossible"); return 0;
    }
    ans+=min(a,b); //两部点取最少
  }
  cout<<ans;
  return 0;
}

 

posted @ 2022-06-28 08:04  董晓  阅读(1239)  评论(0)    收藏  举报