二分图详解

一、二分图基础概念

  1. 定义 二分图(Bipartite Graph)是一种特殊的无向图,其所有顶点可以划分为两个互不相交的集合,满足:图中每一条边的两个端点,都分别属于这两个集合;同一集合内部不存在任何边。 通俗理解:我们可以把图中所有点染成黑白两种颜色,使得任意一条边连接的两个点颜色一定不同,同色点之间没有直接相连的边。
  2. 核心判定定理 一个无向图是二分图,当且仅当图中不存在长度为奇数的环(奇环)。
    偶环(边数为偶数的环):一定可以完成二分染色,属于合法二分图
    奇环(边数为奇数的环):染色必然出现冲突,无法构成二分图 最典型的奇环是三角形(3个点、3条边),三个点两两相连,无论如何染色都会出现相邻点同色的情况。
  3. 常见二分图场景 - 师生关系图、男女配对图 - 跨校交流关系图 - 棋盘黑白格相邻图 - 所有满足"对立关系、分组关系"的无向图

二、算法核心思想

  1. 二分图染色(又称二染色、黑白染色)是判定二分图的标准方法,执行流程如下:
    1. 任选一个未访问的起点,将其染成颜色0
    2. 遍历该点的所有邻居,全部染成颜色1
    3. 继续遍历邻居的邻居,全部染回颜色0
    4. 以此类推,深度/广度优先遍历,交替染色
    5. 遍历过程中若发现相邻点已染色且颜色相同,则判定存在奇环,该图不是二分图
  2. 颜色状态约定 通常用整数数组标记每个点的状态:
    -1:未访问、未染色
    0:第一种颜色(对应A)
    1:第二种颜色(对应B)
  3. 矛盾判定规则 当遍历到某条边的两个端点时: - 若一端未染色:将其染成另一端的反色,继续遍历 - 若两端都已染色且颜色不同:合法,无冲突 - 若两端都已染色且颜色相同:出现矛盾,存在奇环,不是二分图 ## 三、代码实现模板

三、判断一个图是否为二分图模板

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(ll i=a;i<=n;++i)
#define per(i,a,n) for(ll i=n;i>=a;--i)
typedef long long ll;
const int maxn=1e5+10;
vector<ll> G[maxn];
ll n,m,col[maxn],vis[maxn];
bool flag=1;//是否为二分图
bool dfs(ll x,ll c)
{
    col[x]=c;
    vis[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        ll y=G[x][i];
        if(!vis[y])dfs(y,!c);//如果y没有被染色,则染成与x不同的颜色
        else if(col[y]==c)flag=0;//如果y已经被染色,并且与x颜色相同,则说明不是二分图
    }
    return true;
}
void sol()
{
    cin>>n>>m;
    memset(vis,0,sizeof(vis));
    rep(i,1,m)
    {
        ll u,v;
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    rep(i,1,n)
    {
        if(!vis[i])dfs(i,0);
    }
    if(flag)cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}
int main()
{
    sol();
    return 0;
}

四、染色统计与最值计算

  1. 两集合计数方法 在染色过程中增加计数变量,可以统计每个连通块内两种颜色的节点数量

  2. 最小/最大选取结论,每个连通二分图都存在两种合法染色方案:

  3. 起点染0:颜色0数量为cnt0,颜色1数量为cnt1

  4. 起点染1:颜色0数量为cnt1,颜色1数量为cnt0 因此对于单个连通块:某集合最小节点数 = min(cnt0, cnt1),某集合最大节点数 = max(cnt0, cnt1),全局结果只需将所有连通块的对应值累加即可。

五、经典题型与应用模型

  1. 二分图合法性判定 题目特征:要求将对象分成两组,组内不能有联系;或明确要求判断是否为二分图。
    解法:直接染色,出现矛盾输出无解。
    关键词:分组、对立、敌我、不能同组。
  2. 双集合大小统计题目特征:已知关系为跨集合产生,求两个集合的人数范围。
    典型例题:GESP七级「交流问题」------只有不同学校的学生才会交流,求B校最少/最多人数。
    解法:每个连通块染色,分别累加所有块的最小值和最大值。
  3. 最小点覆盖类问题 题目特征:每条边恰好有一端被选中,求最少选中点数。
    典型例题:检查点布置问题------每条道路恰好一端设检查点,求最小数量。
    解法:约束等价于二分图染色,有奇环则无解;无奇环则每个连通块取最小值累加。

六、高频易错点汇总

  1. 初始化错误,颜色数组必须初始化为-1,不能默认用0,否则无法区分"未染色"和"颜色0"。
  2. 遗漏多连通块,** 必须循环遍历所有顶点,只从起点跑一次DFS会漏掉不连通的子图。
  3. 忘记反色递归,邻居必须染成相反颜色!c,漏写反色会导致所有点同色。
  4. 用颜色判断连通性,同一连通块内的点可以同色也可以异色,绝对不能用颜色是否相同判断两点是否连通。连通性判断请使用并查集或连通块编号法。
  5. 大数据栈溢出,节点数量超过范围时,链式图用DFS会爆递归栈,此时必须改用BFS。
  6. 矛盾不及时终止,发现染色冲突后应及时返回,避免无意义递归造成超时。

七、总结

  1. 二分图的本质是"无奇环的无向图",二分染色是判定二分图的标准方法。
  2. 染色算法核心逻辑:"交替染色、冲突判环",DFS和BFS均可实现。
  3. 每个连通二分图有两种合法染色方案,可用于求解集合大小的最值问题。
  4. 二分图染色是GESP、CSP-J等竞赛的高频考点,常结合分组、最值、判定类问题出现,掌握模板后可以快速建模求解。
相关推荐
2401_868534785 小时前
RTOS之RT-Thread & Linux:线程/进程管理与调度核心差异分析
c++·python
wabs6665 小时前
关于图论【卡码网117.软件构建的思考】
数据结构·算法·软件构建·图论·卡码网
mifengxing5 小时前
LeetCode 41.缺失的第一个正数|Hard题O(n)+O(1)最优解法深度解析
java·算法·leetcode·排序算法
郝学胜-神的一滴6 小时前
Qt 高级编程 040:按钮悬浮弹出滑块弹窗的完整攻略
开发语言·c++·qt·软件工程·用户界面
Tongzhi20266 小时前
从部署到运维:通芝科技无感考勤一体机的全流程效率解析
运维·数据结构·科技·算法·贪心算法
Wang's Blog6 小时前
AI Agent白手起家30: 动态示例选择器之根据长度选择 Few Shot 示例
算法
oyguyteggytrrwwwrt8 小时前
自制交叉线路识别算法
算法
手写码匠8 小时前
华为云Flexus+DeepSeek征文|Dify 多智能体协同编排实战:R1 规划 + V3 执行,构建企业 Agent 团队
人工智能·深度学习·算法·aigc
晓天衡宇•评测社区9 小时前
FSR-Bench 前沿科学推理榜单发布:GPT-5.5 居首,“会推理”未必“能答对”
算法
程序喵大人10 小时前
【C++进阶】STL算法与函数对象 - 02 sort为什么需要随机访问迭代器
开发语言·c++·算法