算法补完计划(五) 二分图匹配

本文介绍了二分图的概念,即图可以被分为两个互不相交的子集,子集间有边相连但内部无边。讨论了如何判断一个图是否为二分图,通过染色法证明奇数环的存在会导致无法完成染色。接着讲解了匈牙利算法在二分图匹配中的应用,这是一种递归寻找增广路径的方法,用于找到最大匹配。提供的代码示例展示了如何实现匈牙利算法。最后,建议进一步练习相关题目以加深理解,并提及了学习网络流算法的计划。

二分图

如果一张图能被分为两部分,两部分之间存在边相连,而单个部分内的结点无边相连,那这张图叫做二分图

判断二分图

我们给图进行染色,从一个点开始染成红色,相邻点染蓝色,最后能全部染完,并且任意相邻点颜色不同,说明这是一张二分图。

很好证明:假如从一个点出发,对于一个已经染好色的点,再次访问到时发现需要染另一种颜色,则与开始点形成一个奇数环,这样的话从这个点能染一圈染回来,但是颜色却是矛盾的。

匈牙利算法

之前博哥让我讲过的hh,练练题加深一下

匈牙利算法除了二分图多重匹配之外,在二分图匹配中都能使用,还是比较常用的

大致过程

假如把图分成左右两部分,过程就是令依次遍历左边的结点,去连接右边的结点,如果发现所对应的点已经被之前的点所连,那么让之前的点挪一下(当然,不能挪就别挪了),去连接接下来的点,这是一个递归的过程,递归到能连上为止,如果不行,那说明这个点无法与右边的点匹配。

百度内容:匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法

时间复杂度 O(nm)

代码

板子题:P3386 【模板】二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
int edge[1001][1001], flag[1001], to[1001], n, m, e, ans;
bool find(int x){
    for(int j = 1;j <= m;j ++){
        if(edge[x][j] && flag[j] == 0){
            flag[j] = 1;
            if(to[j] == 0 || find(to[j])){
                to[j] = x;
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    cin >> n >> m >> e;
    for(int i = 1;i <= e;i ++){
        int u, v;
        cin >> u >> v;
        edge[u][v] = 1;
    }
    for(int i = 1;i <= n;i ++){
        memset(flag, 0, sizeof(flag));
        if(find(i))
            ans++;
    }
    cout << ans;
    return 0;
}

后续找点进阶的题做做。。

有时间浅学一下网络流的板子吧


update:改了改板子,用邻接表的版本

#include<bits/stdc++.h>
using namespace std;
struct node{
    int u, v, next;
}edge[110000];
int n, m, e, ans, cnt;
int flag[10001], to[10001], head[10001];
void add(int u, int v){
    edge[++cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt;
}
inline int find(int x){
    for(int i = head[x];i;i = edge[i].next){
        int v = edge[i].v;
        if(flag[v]) continue;
        flag[v] = 1;
        if(to[v] == 0 || find(to[v])){
            to[v] = x;
            return 1;
        }
    }
    return 0;
}
int main(){
    cin >> n >> m >> e;
    for(int i = 1;i <= e;i ++){
        int u , v;
        cin >> u >> v;
        add(u, v);   
    }
    for(int i = 1;i <= n;i ++){
        memset(flag,0,sizeof(flag));
        if(find(i))
            ans++;
    }
    cout << ans << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值