二分图的知识点整理(判断和最大匹配值)

本文深入探讨了二分图的概念,强调了不含奇数环这一重要性质,并介绍了如何通过染色法判断一个图是否为二分图。此外,还详细讲解了匈牙利算法在求解二分图最大匹配问题上的应用,时间复杂度为O(nm)。通过实例展示了如何实现这两种算法,帮助理解二分图在图论中的核心地位。
摘要由CSDN通过智能技术生成

二分图

二分图的定义:

​ 如果一个图是二分图,那么图中所有的点都能够划分到两个集合中,使得点与点之前的边都在两个集合之间,集合内部没有边。
请添加图片描述

二分图的重要性质

1):二分图当且仅当图中不含奇数环。

​ 假设一个环中有n个点,如果n是奇数,那么第1个顶点和第n个顶点肯定会被加入到同一个集合中,即集合内部有边相连,则无法构成二分图;如果n是偶数,那么第1个顶点和第n个顶点肯定会被加入到不同的集合中,即点与点之间的边在两个集合之间,满足二分图的定义,能够构成二分图

2)能够二染色
即把二分图中所有顶点都染成两种颜色,且同一条边相连的顶点颜色不同。

通过染色法判断一个图是否是二分图:

时间复杂度:O(n + m)

思路:

​ 通过对一个图中的每个点和其连接着的点进行染色,如果出现相连的点的颜色相同,即出现矛盾,说明该图无法构成二分图;如果可以将整个图都染完色且不出现矛盾,则该图是二分图。

染色法代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
vector<int>h[N];	//通过动态数组模拟邻接表
int n, m, color[N];	//color数组:记录某个顶点是否被染色

//c:当前染色的编号 1 或 2
int dfs(int u , int c){
    color[u] = c;	
    for(int i = 0; i < h[u].size(); i ++){
        int& j = h[u][i];
        if(!color[j]){
            if(!dfs(j, 3 - c))return 0;	//染色失败,递归返回0
        }   
        else if(color[j] == c)return 0;	//相邻点同色情况,表明染色出现矛盾,无法构成二分图
    }
    return 1;
}
int main()
{
    cin>>n>>m;
    for(int i = 1; i <= m; i ++){
        int a, b;
        cin>>a>>b;
        h[a].push_back(b);
        h[b].push_back(a);
    }
    int flag = 1;	//判断染色是否合法
    for(int i = 1; i <= n; i ++){
        if(!color[i]){
            if(!dfs(i, 1))flag = 0;
        }
    }
    if(flag)cout<<"Yes"<<'\n';
    else cout<<"No"<<'\n';
    
    return 0;
}
匈牙利算法求二分图最大匹配值:

时间复杂度:O(nm)

思路:

​ 设二分图的两个集合为n1和n2.

​ 让n1中顶点u与n2中没有匹配顶点j匹配,如果n1顶点u找到的n2的顶点j匹配过了,就去看能否让与n2的顶点j匹配的n1中的顶点i匹配其他的顶点。如果匹配成功就让匹配数加一,最后输出匹配数即可。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 505;
vector<int>h[N];	//通过动态数组模拟邻接表
int st[N], match[N];	//st:是否找过某个n2的值    match:记录n2中某个值匹配的n1的值
int n1, n2, m;
//find函数:能否为u在n2中找到匹配
int find(int u){
    for(int i = 0; i < h[u].size(); i ++)	//遍历与u相连的n2中的顶点
    {
        int& j = h[u][i]; 
        if(!st[j]){
            st[j] = 1;	//令当前点匹配过
            if(match[j] == 0 || find(match[j]))	//如果该点j没有被匹配过,或者与之匹配的n1中的点可以找其他n2中的点进行匹配,那么我们就让顶点j与当前顶点u匹配
            {
               match[j] = u;
               return true;
            }
        }   
    }
    return false;	//顶点u无法匹配n2中的任何一个顶点
}

int main()
{
    //n1:集合1的顶点数量
    //n2:集合2的顶点数量
    //m:集合1和集合2之间顶点相连的边数
    cin >> n1 >> n2 >> m;	
    for(int i = 1; i <= m; i ++){
        int u, v;
        cin >> u >> v;
        h[u].push_back(v);
    }
    
    int res = 0;
    for(int i = 1; i <= n1; i ++){
        memset(st, 0, sizeof(st));	//初始化:让所有n2中的顶点都没有匹配过
        if(find(i))res ++;	//i匹配成功
    }
    cout << res << '\n';
    return 0;
}
/*
st数组作用:
1)防止当u指向一个被匹配过的顶点j的时候,让匹配该顶点j的顶点i重复找到该点j造成递归死循环
2)st[j]表示当前u找过该顶点j
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值