D24 二分图判定 染色法
如果图 𝐺 =(𝑉,𝐸)
的顶点集 𝑉
可以分为两个互不相交的子集 𝑋
和 𝑌
,使得每条边 𝑒 ∈𝐸
的两个端点都分别属于 𝑋
和 𝑌
,就称图 𝐺
是一个 二分图(bipartite graph).集合 𝑋
和 𝑌
常称作它的两个 部分(part),或者分别称为二分图的左部和右部.当二分图的两个部分 𝑋
和 𝑌
已知时,也可以用三元组 (𝑋,𝑌,𝐸)
来表示二分图 𝐺
.树、偶环、网格图等都是常见的二分图的例子.
性质
- 图 𝐺
是可 2‑着色的.也就是说,可以用至多两种颜色给图的所有顶点染色,并且保证相邻顶点颜色不同. - 图 𝐺
中不存在奇环.

可以考虑用两种颜色尝试给图 𝐺
染色.任选连通分量中的一个顶点 𝑠
,进行 DFS,并记录连通分量中每个顶点 𝑣
与 𝑠
的距离.从 𝑠
开始,在 DFS 生成树上进行归纳可知,如果存在一种可行的染色方法,一定是根据每个顶点 𝑣
到起点 𝑠
的距离的奇偶性分别染成两种颜色.
继而考虑那些不在生成树中的边.如果这些非树边的两个端点的颜色都不一样,就说明当前的染色方案可行;否则,就不存在可行的方案.
进一步地,两个顶点颜色不同,当且仅当它们到树根 𝑠
的距离一奇一偶,这又等价于加入该非树边形成的是一个偶环而非奇环.因此,只要没有奇环,这些非树边必然连接颜色不同的点,进而整张图都可以用两种颜色染色,图就一定是二分图.
判定
判定一个图是不是二分图,尝试给二分图染色即可.可以使用 DFS 或者 BFS 来遍历这张图.如果发现了奇环,也就是出现无法染色的情况,那么就不是二分图;否则,就是二分图.
- 遍历顶点,如果发现还没有染色的顶点,说明发现新的连通分量.
- 任选一种颜色给该顶点染色,并以它为起点做 DFS 或者 BFS,尝试给该连通分量染色.
- 如果发现尚未染色的顶点,将尚未染色的顶点染上与当前顶点相反的颜色.
- 如果发现已经染色的顶点,检查颜色是否与当前顶点相同.相同,则不是二分图,直接返回;否则,继续遍历.
应用
很多图论优化问题都可以在二分图上高效解决.
- 极大团(平凡)
- 最小点着色(平凡)
- 最小边着色
- 最大匹配
- 最小边覆盖
- 最小点覆盖
- 最大独立集
- 最大权匹配
- 二分图博弈
// 染色法判定二分图 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; }
// 染色法判定二分图 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; }
浙公网安备 33010602011771号