从一道 GESP 真题出发:聊聊环图染色与二分图判定
2026/8/24 4:04:51 网站建设 项目流程

从一道 GESP 真题出发:聊聊环图染色与二分图判定

题源链接:洛谷 P17014 [GESP202606 七级] 染色


一、背景

在图论的世界里,有一类问题看似在问"最少需要几种颜色",实则是在考你对图的结构有多深的理解。GESP 七级的这道染色题,就是一个绝佳的例子。

题目给了一个看似不起眼的条件:每个结点的度数都是2 22。这个条件就像一把钥匙,一旦你用对了,整道题的结构就会像剥洋葱一样层层展开。但如果没意识到这个条件的威力,你可能会一头扎进复杂的图染色算法里,比如尝试用四色定理、回溯搜索、甚至网络流——这些在本题里都是大炮打蚊子。

本文就从这道染色题出发,聊聊度数约束下的图结构分析环图的染色性质,以及二分图判定这个经典思想在其中的应用。


二、核心思想

2.1 度数约束:从混乱到秩序

拿到这道题,很多选手的第一反应可能是"建图、跑染色、求色数"。但如果我们先停下来,仔细品味一下题目给出的条件——每个结点的度数都是2 22,就会发现一个惊人的事实:

在一个无重边、无自环的无向图中,如果每个结点都恰好有两条边相连,那这个图必然由若干个不相交的简单环组成。

为什么?想象你在一个迷宫里行走,每个路口(结点)都恰好有两条路(边)可以走。你从任意一个路口出发,沿着路走,因为每个路口只有两条路,你不可能"分叉",只能一直走下去。由于图是有限的,你最终一定会回到某个已经走过的路口——而因为无重边,你只能回到起点。于是你画出了一个环。如果还有没走过的路口,重复这个过程,最终整个图就被分解成了若干个互不相交的环。

这个推导过程的特征:

  • 从局部性质推导全局结构:单个结点的度数约束,决定了整个图的连通块形态
  • 无需显式找环:只要知道每个连通块是环,就能直接利用环的性质
  • 化繁为简:将复杂的"图染色"问题,转化为简单的"环的奇偶性判定"问题

2.2 环的染色:二分图的直觉

知道了图由若干个环组成,下一步就是回答:一个环最少需要几种颜色?

这里要引入一个图论中的核心概念——二分图(Bipartite Graph)。二分图的定义是:可以把所有结点分成两组,使得每条边的两个端点分别属于不同的组。换句话说,二分图可以用2 22种颜色染色,且相邻结点颜色不同。

那么,什么样的环是二分图?

  • 偶环(结点数是偶数):可以交替染色,比如A o B o A o B ⋯ A o B o A o B \cdotsAoBoAoB,回到起点时颜色一致。所以偶环是二分图,色数为2 22
  • 奇环(结点数是奇数):无论如何交替,回到起点时颜色都会冲突。所以奇环不是二分图,色数为3 33

我们可以把偶环想象成一个钟摆:左右来回摆动,偶数次后回到原位,状态一致;奇环则像一个拧了一半的魔方:你转了一圈,发现对不上,必须引入第三种颜色来"解围"。

2.3 全局最优:各连通块独立,取最大值

整个图由若干个不相交的环组成,每个环独立染色,颜色可以在不同环之间复用。因此,整个图的最少颜色数,等于所有连通块色数的最大值

这就像你有若干个独立的调色盘,每个调色盘上的颜色可以和其他调色盘重复。你需要的最多种颜色数,取决于"最难染"的那个连通块。


三、算法模板

3.1 算法到底在干什么?——直觉解释

我们的算法本质上是一台"图结构扫描仪":

  1. 扫描连通块:从每个未访问的结点出发,用 DFS 遍历整个连通块,统计结点数
  2. 判定环的奇偶:根据结点数是奇数还是偶数,判定该环需要2 22色还是3 33
  3. 汇总取最大:所有连通块中,取所需颜色数的最大值作为答案

整个过程就像给一张地图上的每个岛屿(连通块)分配一个"难度等级",最终答案取决于最难的那个岛屿。

3.2 万能模板 —— 伪代码 + 实战代码

伪代码:

function 环图最少染色数(G): ans = 0 for each node i in G: if i not visited: cnt = dfs_count(i) // 统计连通块大小 if cnt % 2 == 0: res = 2 // 偶环 else: res = 3 // 奇环 ans = max(ans, res) return ans function dfs_count(x): mark x as visited cnt = 1 for each neighbor y of x: if y not visited: cnt += dfs_count(y) return cnt

实战代码(通用模板):

#include<bits/stdc++.h>usingnamespacestd;constintN=100005;// 根据题目数据范围设定intn;vector<int>g[N];// 邻接表boolvis[N];// 访问标记intcnt;// 连通块结点数voiddfs(intx){vis[x]=true;cnt++;for(inty:g[x]){if(!vis[y])dfs(y);}}intmain(){intt;cin>>t;while(t--){cin>>n;for(inti=1;i<=n;i++)g[i].clear();for(inti=1;i<=n;i++){intu,v;cin>>u>>v;g[u].push_back(v);g[v].push_back(u);}memset(vis,0,sizeof(vis));intans=0;for(inti=1;i<=n;i++){if(!vis[i]){cnt=0;dfs(i);if(cnt%2==1)ans=max(ans,3);elseans=max(ans,2);}}cout<<ans<<endl;}return0;}

3.3 例题实现 —— 本题完整代码

#include<bits/stdc++.h>usingnamespacestd;constintN=100005;// 常量:最大结点数intt;// t: 数据组数intn;// n: 当前数据的结点数vector<int>g[N];// g[x]: 结点 x 的邻接结点列表boolvis[N];// vis[x]: 标记结点 x 是否已被访问intcnt;// cnt: 当前连通块的结点数voiddfs(intx)// 深度优先搜索,统计连通块大小{vis[x]=true;// 标记当前结点已访问cnt++;// 连通块结点数加一for(inti=0;i<g[x].size();i++)// 遍历所有邻接结点{inty=g[x][i];// y: 邻接结点if(vis[y])// 如果已访问,跳过continue;dfs(y);// 递归搜索}}intmain(){cin>>t;// 读入数据组数while(t--)// 循环处理每组数据{cin>>n;// 读入结点数for(inti=1;i<=n;i++)// 清空邻接表g[i].clear();for(inti=1;i<=n;i++)// 读入 n 条边{intu,v;// u, v: 边的两个端点cin>>u>>v;g[u].push_back(v);// 建立无向图g[v].push_back(u);}memset(vis,0,sizeof(vis));// 清空访问标记intans=-1;// ans: 最少需要的颜色数intres;// res: 当前连通块需要的颜色数for(inti=1;i<=n;i++)// 枚举每个结点,处理所有连通块{cnt=0;// 重置连通块计数器if(!vis[i])// 如果结点 i 未被访问(新的连通块)dfs(i);if(cnt%2==1)// 如果连通块结点数为奇数res=3;// 奇环需要 3 种颜色else// 如果连通块结点数为偶数res=2;// 偶环只需要 2 种颜色ans=max(ans,res);// 取所有连通块颜色数的最大值}cout<<ans<<endl;// 输出最少需要的颜色数}return0;}

3.4 对比实现 —— 其他路径的探讨

本题的核心在于"度数约束推结构",但如果不利用这个性质,还有哪些思路?

方案核心思想时间复杂度适用场景
DFS 统计连通块 + 奇偶判定(本题做法)利用度数约束推导出环结构O ( n ) O(n)O(n)度数恰好为2 22的图
显式找环 + 判定奇偶Tarjan / DFS 找环,记录环长O ( n ) O(n)O(n)需要知道具体环的构成
二分图判定(BFS 染色)尝试用2 22色染色,检测冲突O ( n ) O(n)O(n)通用图的二分图判定
回溯染色(通用图染色)暴力尝试所有染色方案指数级小规模图,无特殊结构

对于本题,DFS 统计连通块是最直接的方法。但值得一提的是,**二分图判定(BFS 染色)**也是一个非常优雅的替代方案:尝试用2 22色给图染色,如果遇到冲突(相邻结点同色),则说明存在奇环,答案至少为3 33。这种方法更具通用性,适用于任意图的二分图判定。

3.5 变体清单 —— 常见变形

变体类型题目描述关键变化解法调整
度数不固定的一般图任意无向图,求最少染色数图结构任意四色定理(平面图4 44色),一般图是 NP-hard
度数约束为1 11每个结点度数为1 11图由若干条不相交的链组成每条链最多2 22色,孤立点1 11
度数约束为k kk每个结点度数为k kkk kk-正则图用 Brooks 定理:色数≤ k \leq kk(除完全图和奇环)
有向图版本有向图,要求弧两端颜色不同无向边变为有向弧转化为无向图后同解
带权染色每种颜色有代价,求最小代价染色目标函数变化动态规划或整数规划
在线加边动态加边,每次查询当前最少颜色数图动态变化并查集维护连通块,或线段树分治

3.6 什么时候不能用?——边界条件和反例

本题的方法依赖于"每个结点度数为2 22"这一强约束,一旦条件变化,思路需要大幅调整:

  • 度数不固定时:如果图的度数任意,图的结构可能是树、一般图、甚至稠密图。此时最少颜色数的计算是 NP-hard 问题,没有多项式时间算法。
  • 有重边或自环时:题目保证无重边、无自环。如果有自环,那个结点必须和自己颜色不同,这是不可能的,问题无解。如果有重边,不影响结论,但需要注意建图时去重。
  • 只有一个结点时n = 1 n = 1n=1,度数为0 00(不满足度数为2 22的条件,但题目保证度数为2 22,所以n ≥ 3 n \geq 3n3)。如果单独考虑,1 11个结点只需要1 11种颜色。
  • 多个连通块颜色复用的误区:有同学可能会想"每个连通块独立算,然后加起来"。这是错的!颜色可以在不同连通块之间复用,应该取最大值,而不是求和。

四、底层逻辑

4.1 为什么度数约束能推导出环结构?

这是一个严谨的图论结论。在无向图G = ( V , E ) G = (V, E)G=(V,E)中,如果每个结点的度数d e g ( v ) = 2 deg(v) = 2deg(v)=2,且无重边、无自环,则:

  1. 从任意结点v 0 v_0v0出发,有两条边可以走,选择其中一条到达v 1 v_1v1
  2. v 1 v_1v1处,有一条边回到v 0 v_0v0,另一条边到达v 2 v_2v2(不能回到v 0 v_0v0后终止,因为v 1 v_1v1度数为2 22
  3. 重复这个过程,由于图是有限的,必然存在某个v k = v i v_k = v_ivk=vii < k i < ki<k
  4. 由于无重边,v k v_kvk只能通过一条边回到v i v_ivi,而这条边只能是v k − 1 o v i v_{k-1} o v_ivk1ovi,即v i = v 0 v_i = v_0vi=v0
  5. 因此形成简单环v 0 o v 1 o ⋯ o v k − 1 o v 0 v_0 o v_1 o \cdots o v_{k-1} o v_0v0ov1oovk1ov0
  6. 如果还有未访问的结点,重复上述过程

这就证明了图必然由若干个不相交的简单环组成。

4.2 与经典问题的对比

这道题和经典的"图染色"问题家族有密切联系:

问题图结构色数解法
树染色树(无环)2 22二分图,BFS 交替染色
本题:2-正则图染色不相交环的并2 223 33环长奇偶判定
二分图判定任意图2 22(若是二分图)BFS/DFS 染色,检测冲突
一般图染色任意图未知(NP-hard)近似算法、回溯、启发式
平面图染色平面图≤ 4 \leq 44(四色定理)复杂的多项式算法

可以看到,度数约束将问题从 NP-hard 的深渊拉回到了O ( n ) O(n)O(n)的线性时间可解。这就是图论中"利用结构简化问题"的经典范例。

4.3 隐含约束的分析

题目中有几个容易被忽略但至关重要的细节:

  • 多组数据t tt组数据,每组需要清空邻接表和访问标记数组。忘记清空是常见的 WA 原因。
  • n nn条边:题目说"每个结点度数为2 22",意味着m = n m = nm=n(边数等于结点数)。这是环图的一个特征(树的边数是n − 1 n-1n1,环图是n nn)。
  • 无重边、无自环:保证了图是简单图,从而度数约束能严格推导出环结构。
  • 颜色可复用:不同连通块之间颜色可以复用,所以答案是各连通块色数的最大值,而非总和。

五、决策表

面对"图染色"类问题,如何根据图的结构特征快速选型?

图结构特征最少颜色数推荐方案时间复杂度
树 / 森林2 22BFS 交替染色O ( n ) O(n)O(n)
2-正则图(不相交环)2 223 33DFS 统计连通块 + 奇偶判定O ( n ) O(n)O(n)
二分图2 22BFS/DFS 染色,检测冲突O ( n + m ) O(n + m)O(n+m)
一般图(小数据)未知回溯 / 分支限界指数级
平面图≤ 4 \leq 44四色定理算法(复杂)多项式
完全图K n K_nKnn nn每个结点颜色不同O ( 1 ) O(1)O(1)判定

一句话总结:先看结构,再定色数;有约束用约束,无约束上暴力。


六、工程视角

图染色和连通块分析的思想在实际工程中有着广泛的应用:

  1. 寄存器分配(编译器优化):编译器在生成机器码时,需要将变量分配到有限的寄存器中。这可以建模为图染色问题:每个变量是一个结点,如果两个变量同时活跃则连边,每种颜色代表一个寄存器。对于循环结构(类似本题的环),编译器会利用环的周期性来优化寄存器复用。

  2. 无线信道分配:在蜂窝网络中,相邻基站不能使用相同频率,否则会产生干扰。将基站建模为图的结点,相邻基站连边,频率分配就是一个图染色问题。对于环形拓扑的网络(如地铁沿线的基站),本题的结论直接适用:偶数个基站只需2 22个频率交替使用,奇数个需要3 33个。

  3. 考试时间表编排:学校安排期末考试时,如果两门课有共同的学生,就不能安排在同一时间。将课程建模为结点,有共同学生的课程连边,时间段就是颜色。对于某些特殊结构的课程依赖图(如循环先修课关系),可以用类似本题的思路快速判定最少时间段数。

  4. 死锁检测(操作系统):在资源分配图中,如果存在一个环,就可能发生死锁。通过检测图中的环结构(类似本题的 DFS 遍历),操作系统可以预判并避免死锁。每个连通块的独立性也意味着不同资源池之间的死锁可以独立分析。


七、小结

本文从一道 GESP 七级真题出发,探讨了度数约束下的图结构分析环图染色问题。

核心认知可以总结为:

当图的局部性质(度数约束)足以决定全局结构时,直接分析结构比套用通用算法更高效;环的奇偶性决定了它的二分图属性,进而决定了色数。

用公式化的语言概括:

KaTeX parse error: Unexpected character: '' at position 40: …ext{每个连通块 } C} ̲egin{cases} 2, …

其中C CC是图的每个连通块(环),∣ C ∣ |C|C是该连通块的结点数。

这道题教会我们的,不仅是如何写 DFS 和判断奇偶,更是一种**“读题先读条件”**的思维习惯:在算法竞赛中,题目给出的每一个条件都可能是解题的钥匙。度数为2 22这个看似普通的约束,实际上把整个问题从 NP-hard 的图染色问题,简化为了O ( n ) O(n)O(n)的线性扫描。这种"从约束到结构,从结构到算法"的推导链条,是图论问题中最优雅的解题路径。


如果这篇文章对你有帮助,欢迎点赞收藏!有任何问题欢迎在评论区留言交流。

标签:#GESP #算法竞赛 #图论 #DFS #二分图 #图染色 #环图 #连通块 #C++ #洛谷

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询