二分图匹配

二分图匹配

首先还是要了解二分图匹配是个什么东西

​ 分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

​ (以上来自百度百科)说简单一些就是对于一个无向图可以将它分为两个集合,对于同一集合内的元素一定没有边相连,而不同集合内的元素存在边相连,这样的图称之为二分图。

那么二分图匹配呢?

​ 给定一个二分图,寻找与一个边集的边能够满足任意两条边都不依附在同一个顶点,这样叫做二分图的一个匹配。

​ 说的更简单一些也就是让二分图两个集合内的点一个一个对应起来。

​ 通常做题时我们对于一个二分图需要求它的一个最大匹配。也就是尽可能让边集中边的数量尽可能的多,尽可能多的点能够匹配在一起。

匈牙利算法

​ 求解一个二分图的最大匹配,比较简单也是常用的办法就是匈牙利算法。

​ 算法证明比较复杂,所以下面只介绍算法实现的流程。

​ 1.让第一个集合中的点去匹配

​ 2.如果匹配到的点没有配对,那么就暂时先和这个点匹配起来

​ 3.如果匹配到的点已经配对了,那么尝试让与匹配到的这个点匹配的点再去匹配,如果能匹配到,则当前点和匹配到的点就匹配到一起,否则就不能。

​ 4.依次再对集合中的下一个点进行这样的匹配操作。

需要注意的是:每一轮操作中,一个点只能被匹配一次。

例题

luogu 2055假期的宿舍 (一道板子题就不多说了x

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int read(){
    int a=0,f=0;char p=getchar();
    while(!isdigit(p)){f|=p=='-';p=getchar();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
    return f?-a:a;
}
void print(int x){
    if(x<0)putchar('-');x=-x;
    if(x>=10)print(x/10);
    putchar(x%10+'0');
}
int flag1[60],flag2[60];
int n;
vector<int> m[100];
bool gx[100][100];
bool vis[100];
int bf[100];
bool dfs(int x){
    for(int i=0;i<m[x].size();i++){
        int j=m[x][i];
        if(vis[j])continue;
        vis[j]=1;
        if(!bf[j]||dfs(bf[j])){
            bf[j]=x;
            return true;
        }
    }
    return false;
}
int main(){
    int t;
    t=read();
    while(t--){
        int cnt=0;
        memset(flag1,0,sizeof(flag1));
        memset(flag2,0,sizeof(flag2));
        memset(m,0,sizeof(m));
        memset(gx,0,sizeof(gx));
        memset(bf,0,sizeof(bf));
        n=read();
        for(int i=1;i<=n;i++)
            flag1[i]=read();//如果是1表示是在校学生
        for(int i=1;i<=n;i++){
            flag2[i]=read();//1表示这个在校学生要回家睡觉
            if(flag1[i]==0)flag2[i]=1;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                gx[i][j]=read();
                if(gx[i][j]){
                    if(flag1[j])m[i].push_back(j);//如果j在学校有床
                }
                if(i==j&&flag1[i])m[i].push_back(j);
                
            }
        }
        for(int i=1;i<=n;i++){
            if((!flag1[i])||(!flag2[i])){//不是在学生或者他不回家睡觉
                memset(vis,0,sizeof(vis));
                if(!dfs(i))cnt++;
            }
        }
        if(cnt)cout<<"T_T"<<endl;
        else cout<<"^_^"<<endl;
    }
}
posted @ 2020-04-18 13:22  蕙心心w  阅读(231)  评论(0编辑  收藏  举报