二分图判定&二分图最大匹配模板

二分图定义

在这里插入图片描述
可以把所有的点划分成两个集合,集合内部不存在边,集合之间存在边;

01染色判定二分图

性质

  • 一个图是二分图等价于不存在奇数环

奇数环是指点的个数是奇数的环,如下图;

在这里插入图片描述

有一个通俗的解释,奇数个点无法被两种颜色均分


那么我们的做法很简单,如果一个点没有被染色,那么我们将它(记为 u u u)染为白色,将与 u u u相连的点染成黑色;

不断重复这个过程即可,若过程中发现同色相连,那么就产生矛盾了;

染色法判二分图

传送门

题面

在这里插入图片描述

时间复杂度

O ( N + M ) , N 为 点 的 个 数 , M 为 边 的 个 数 O(N+M),N为点的个数,M为边的个数 O(N+M),N,M

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int color[N];
vector<int> G[N];
int n,m;
bool dfs(int u,int c){
    color[u] = c;
    for(auto to : G[u]){
        if(color[to] != -1){
            if(color[to] == c) return false;
        }
        else{
            if(!dfs(to,c^1)) return false;
        }
    }
    return true;
}
void solve(){
    cin >> n >> m;
    while(m--){
        int u,v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;++i) color[i] = -1;
    bool ff = 1;
    for(int i=1;i<=n;++i){
        if(color[i] == -1){
            ff = dfs(i,0);
            if(!ff) break;
        }
    }
    if(ff) cout << "Yes\n";
    else cout << "No\n";
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}

匈牙利算法

用于求解二分图的最大的成功匹配;

成功匹配指的是,两个点一一配对,不存在脚踩多条船的情况;

也可以称为,月老算法

比如我们是月老,问我们最多能给多少对男女牵红线(一夫一妻!!!);

在这里插入图片描述

算法过程是这样的,如果一个点可以匹配另一个点,那么我们直接匹配,皆大欢喜;

如果不能匹配,比如说 u u u的心仪对象是 v v v,但是 v v v已经和 w w w配对了;那么 u u u就会尝试把 w w w绿了,但是绿的前提是 w w w能找到另外的新欢,比如说 k k k

那么 u , v u,v u,v配对, w , k w,k w,k配对;

比如说下图的左三就绿了左一,抢了右二…(NTR)

在这里插入图片描述

二分图的最大匹配

题面

传送门
在这里插入图片描述

时间复杂度

O ( N ∗ M ) , N 为 点 数 , M 为 边 数 O(N*M),N为点数,M为边数 O(NM),N,M,实际时间没有这么大;

Code

注意一个地方,虽然二分图是无向图,但是我们这里存图只用存左边到右边,因为只用到了左边到右边;

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 510 + 10;

int n1,n2,m;

vector<int> G[N];
bool st[N];//右边的每个点是否访问过
int match[N];//右边点匹配的点是谁
bool dfs(int u){
    for(auto to : G[u]){
        if(st[to]) continue;
        st[to] = 1;//右边每个点只考虑一次
        //如果没有匹配,或者匹配的人另寻新欢
        if(match[to] == 0 || dfs(match[to])){
            match[to] = u;
            return 1;
        }
    }
    return 0;
}
void solve(){
    cin >> n1 >> n2 >> m;
    while(m--){
        int u,v;
        cin >> u >> v;
        G[u].push_back(v);
    }
    //为左边每个点匹配
    int res = 0;
    for(int i=1;i<=n1;++i){
        memset(st,0,sizeof st);
        if(dfs(i)) ++res;
    }
    cout << res;
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值