如果所有“不能同队”的关系都能安排成左队和右队,图就是二分图。听起来像排座位,真正的捣蛋鬼却是一种很具体的结构:奇数长度的圈。
学完后,你能:
前置:无向图、BFS/DFS、连通分量。
先不要把颜色当漂亮的涂料。给每个点留一个格子,填 未定 / 0 / 1;每处理一条边,就把它读成约束:“若 u 是 0,v 必须是 1。”纸上同时记录 是谁迫使谁染色,这样冲突时才能沿证据回去找圈。
本节只有一个框架问题:局部的“不同”约束能否一路传播而不自相矛盾?先让自己撞到矛盾,再给它正式名字“奇环”。
画路径 A-B-C-D。任意给 A 涂 0,沿边依次写出其余颜色。然后把最后一条边 D-A 加上,观察结果;再把路径改为 A-B-C-A。
停顿 0:为什么长度为 4 的环可以回到 A 的原色,而长度为 3 的环会要求 A 既是 0 又是 1?请逐边写颜色,不要直接说“奇环”。
画边 A-B,A-D,C-B,C-D,E-F。从 A 开始,给 A 染 0;每遇到未染邻点就染反色。请填写:
| 当前取出的点 | 它的颜色 | 看到的未染邻点 | 给出的颜色 | 父亲证据 |
|---|---|---|---|---|
| A | 0 | |||
| B | 1 | |||
| E | 0 |
停顿 1:为什么处理完 A 所在分量后,仍要从 E 重新开始?
visited与color在这里分别负责什么?
在图 A-B,B-C,C-A,C-D 中,从 A 染 0。你会得到 B、C 都是 1,然后检查边 B-C。请保存每个点的父亲:parent[B]=A,parent[C]=A。
停顿 2:从 B 沿父亲回根、从 C 沿父亲回根,再加冲突边 B-C,会得到哪一圈?为什么它长度是奇数?
把可二染分量的根 A 从 0 改成 1,不重新搜索,只把所有点颜色翻转。请检验每条边是否仍跨色。然后试图只翻转 C,观察哪条边先坏。
停顿 3:题目若要求恰好 K 个点在左侧,为什么不能把每个点随意改色?真正可自由选择的单位是什么?
若边表示“左侧任务可交给右侧机器”,或“相邻点不能同组”,请分别写出颜色 0/1 在题面中的角色。再问:若输入本身已经把对象分成男/女、工人/任务两类,还需要先跑二染吗?
给每个点猜“左/右”看似简单,但 $n$ 个点有 $2^n$ 种分配。边提供了更强的传播规则:给一个起点染 0,沿每条边的另一端必须是 1,再沿下一条边又必须是 0。一次 BFS/DFS 就能把一个连通块的所有颜色推出来。
真正需要检查的是:某条边的两端会不会被迫同色。若会,沿 BFS/DFS 树从两端回到共同祖先,再加上这条冲突边,正好得到一个奇环。
沿一个环交替染色。走偶数步回到起点会要求原色;走奇数步回到起点却要求反色,矛盾。所以任何奇环都不是二分图。
反过来,若交替染色遇到同色边 $(u,v)$,DFS/BFS 树上从根到 $u$、从根到 $v$ 的路径长度同奇偶;两条路径加这条边形成奇环。因此“能二染”与“没有奇环”是充要关系,而不是一条单向小技巧。
color[all] = UNCOLORED
for every vertex start:
if color[start] is UNCOLORED:
color[start] = 0
queue.push(start)
while queue not empty:
u = queue.pop_front()
for v in neighbors[u]:
if color[v] is UNCOLORED:
color[v] = 1 - color[u]
queue.push(v)
else if color[v] == color[u]:
report "not bipartite"
对 A-B,B-C,C-A,C-D,从 A=0 开始,填写:
| 队列取出 | color 已知值 |
处理的边 | 采取动作 | parent 新记录 |
|---|---|---|---|---|
| A | A-B | |||
| A | A-C | |||
| B | B-C |
然后在另一个纸面上给 A-B,A-D,C-B,C-D,E-F 染色;把两个分量的两侧大小写为 (2,2) 与 (1,1),试着整体翻转其中一个。
确定根的颜色后,任一点到根的路径长度奇偶决定其颜色;若图无奇环,任意两条路径的奇偶不会矛盾。因此整个连通分量只有“当前 0/1 分配”和“全部交换 0/1”两种选择。多个分量互不影响,才会出现“每团选左还是右”的背包或构造问题。
二分匹配只在左右两侧间配对:一条边表示“这个左对象可以和这个右对象搭档”。今天不求最大匹配,只先练模型辨认:若对象天然有两类、关系只应跨类出现,先检查二分结构;若图不是二分图,别急着套匹配。
color[u] 不是一个随便的标签,而是“从起点到 $u$ 的路径长度奇偶”的证书。每次把未染邻点设成反色,都满足当前边;遇到同色边则给出不可行的具体证据。全图要从每个未染点启动,因为非连通分量没有边替你传话。
时间和空间都是 $O(V+E)$。自环立即连接同一点与自己,强迫它与自己异色,因此是最短的奇环;重边不会破坏二分性,只是重复检查同一约束。
-1 表示未染,避免把颜色 0 误当“没访问”。parent 和 depth 能在冲突时还原一个奇环,调试非常有用。运行 两支队伍与奇怪的圈.py。选择 g 看一个可二染图如何分左右两侧;选择 b 看一条同色冲突边如何还原奇环。也可输入自己的无向边表。
建议先挑一个三角形,试图亲手给三点染色;当第三条边开始抗议时,再让程序把它说出来。
连通二分分量的翻转会通向分组背包;交替路径会通向二分匹配;“没有奇环”还会出现在图的奇偶约束和差分建模中。若边约束不只是“不同”,例如要求颜色差为某个模数,就需要更一般的带权并查集或图染色工具。