二分图判断&最大匹配

二分图

  • 二分图
  • 什么是二分图
  • 染色法判断二分图
  • 什么是二分图最大匹配
  • 匈牙利算法
  • 例题

定义:二分图(特殊图结构),图中的顶点集可以被划分为两个互不相交的子集,且图中边集中每条边的两个端点分别属于两个不同的子集,则称该图为二分图。

二分图中允许偶环存在,不允许奇环存在(偶环就是构成环的边数为偶数条,奇环则边数为奇数)。

仔细想,从一个顶点集的某个顶点出发,走过奇数条边到另一个顶点集,走过偶数条边回到当前顶点。因为每条边的两个端点分别属于两个不用的子集,所以一定符合这种情况。

怎样判定当前图是不是二分图?

染色法:

思路就是dfs或bfs一遍图,用一个全局数组来标记所有点的颜色,1表示第一个集合颜色,里面每个点的颜色都应该为1, 2用来表示另一个集合中点的颜色,相应里面每个点的颜色都应该为2。若x节点颜色为c, 依次遍历x的每一条边,边的另一个端点y:若y此时还没有颜色标记,则用 3 − c 3 - c 3c去遍历染色y;若y此时有颜色标记,若y的颜色==x的颜色,说明该图不是二分图,因为出现了一条边两个端点属于同一个集合中。

实现代码如下:

#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> pii;
const int N = 1e5 + 10;

vector<int> h[N];
vector<int> color(N, 0);

void add(int a, int b) {
    h[a].push_back(b);
} 
bool dfs(int x, int c) {
    color[x] = c;
    for(int y : h[x]) {
        if(!color[y]) {
            if(dfs(y, 3 - c)) return true;
        }
        else {
            if(color[y] == c) return true;
        }
    }
    return false;
}
int main() {
    int n, m; cin >> n >> m;
    for(int i = 0; i < m; i++) {
        int a, b; cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    bool flag = false;
    for(int i = 1; i <= n; i++) {
        if(!color[i]) {
            if(dfs(i, 1)) flag = true;
            break;
        }
    }
    if(flag) puts("No");
    else puts("Yes");
    return 0;
}

什么是二分图最大匹配? //推荐视频【D25 二分图最大匹配 匈牙利算法】https://www.bilibili.com/video/BV1GW4y1z7xk?vd_source=4c9eb38d8205116069b961c84f64c958

匈牙利算法:

o(n * m)

在这里插入图片描述
Alt

在上面链接视频中,董晓算法老师形象将二分图分割的两个顶点集合划分为男女相亲中的男生集合和女生集合,这里建边只用建单向边,通过对男生占,男生让来实现不断动态找增广路,能让则让,就会增加条边的匹配。

例题:P3386 【模板】二分图最大匹配 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

实现代码:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N = 500 + 10;

int n, m, e, ans;
int vis[N], match[N]; 
vector<int> h[N];

int dfs(int x) {
    for(int y : h[x]) {
        if(vis[y]) continue;
        vis[y] = 1;
        if(!match[y] || dfs(match[y])) {
            match[y] = x;
            return 1;
        }
    }
    return 0;
}
int main() {
    cin >> n >> m >> e;
    for(int i = 1; i <= e; i++) {
        int u, v; cin >> u >> v;
        h[u].push_back(v);
    }    
    for(int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof vis);
        if(dfs(i)) ans ++;
    }
    cout << ans;
    return 0;
}

C-有大家喜欢的零食吗_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

实现代码:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N = 500 + 10;

int n, ans;
vector<int> e[N];
int vis[N], match[N];

int dfs(int x) {
    for(int y : e[x]) {
        if(vis[y]) continue;
        vis[y] = 1;
        if(!match[y] || dfs(match[y])) {
            match[y] = x;
            return 1;
        }
    }
    return 0;
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int k; cin >> k; 
        for(int j = 1; j <= k; j++) {
            int x; cin >> x;
            e[i].push_back(x);
        }
    }
    for(int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof vis);
        if(dfs(i)) ans++;
    }
    if(ans == n) cout << "Yes";
    else cout << "No\n" << (n - ans); 
    return 0;
}
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幻听嵩的留香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值