3.30 二分图

什么是二分图?

二分图的定义是:
设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。简而言之,就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。
参考二分图详解

如何判断二分图?

一个图是二分图当且仅当图中不含有奇数环
原理:
证明必要性:
如下图,假如图中存在一个奇数环(即该环上点的个数为奇数)
假设A属于集合0,那么由于A和B相连,B就属于集合1,同理C属于集合0。
然而C与A相连,因此A属于集合1,这和我们的假设矛盾,因此可以说明二分图中一定没有奇数环
在这里插入图片描述

证明充分性:
创建一种构造方法:
遍历所有点,如果没有被染色,那就将它放在左边的集合中(染成红色),然后遍历该点的所有相连的点,将所有相连的点放到右边的集合中(染成绿色)。直到所有都被染色。
如果这样的方法能够构造成功,那么一定会生成一个二分图。
由于不含有奇数环,因此一定不会矛盾。(不会发生矛盾染色)

如何用代码判断二分图?

根据上面的思想,如果一个图可以在染色中顺畅地无误地染色完成,那就说明其是二分图。如果不能,则说明其不是二分图

写代码的时候憨比了,把对h的初始化放在了add后面。这样不就等于add之后又初始化h,相当于屁都没做


#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1e5+10;
const int M=2*1e5+10;

int h[N],ne[M],e[M],idx=1;
int colour[N];

int n,m;

void add(int a,int b){
    
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
    
}

//颜色共有1,2两种颜色
int dfs(int u,int c){
    //将节点u染成c颜色
    colour[u]=c;
    
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(colour[j]==0){
            //如果未被染色
            if(!dfs(j,3-c))   return 0;
        }
        else if(colour[j]==c)   return 0;
    }
    return 1;
}


int main(){
    
    cin>>n>>m;
    memset(h,-1,sizeof h);
    
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);add(b,a);
    }
    
    
    int flag=1;
    
    for(int i=1;i<=n;i++){
        if(!colour[i]){
            if(!dfs(i,1)){
                flag=0;
                break;
            }
        }
    }
    
    if(flag==0)
        cout<<"No"<<endl;
    else
        cout<<"Yes"<<endl;
    
    return 0;
}

匈牙利算法求二分图最大匹配

什么是最大匹配?

二分图的匹配:给定一个二分图 G,在 G的一个子图 M中,M的边集 {E}中的任意两条边都不依附于同一个顶点,则称 M是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

匈牙利算法思路

把二分图的左边集合看作男生,右边集合看作女生,集合之间的连线看作男生女生互有好感,每次确定一条联系就是双方真正谈恋爱。这样每一个匹配都是一个男生和一个女生谈,双方同时都不能和另外的人谈,这样就符合匹配的定义。
匈牙利算法的过程就是,遍历所有的男生(或女生),对于那些没有谈恋爱的男生,找和他互有好感的女生(即查找自己连接的边),如果那个女生当前没有谈恋爱,那么太好了!就让这一对谈恋爱。(即构成一对匹配)
如果那个女生谈恋爱了,那么这个男生也不能放弃,他需要找到现在和这个女生谈恋爱的男生A,问他能不能找别人谈,如果A同时也和其他人有好感并且对方没有谈恋爱,那么就让A和另一个女生谈,这个男生就能追到心仪的女孩了。
就这样匹配下去,直到没人可谈了。

可以看出算法时间复杂度是O(n*m),n是左边子集数量,m是右边子集数量


#include<iostream>
#include<cstring>

using namespace std;

const int N=600,M=1e5+10;

int n1,n2,m;
int e[M],ne[M],h[N],idx=1;
int match[N];
int st[N];

void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
}

int find(int t){
    //如果给t匹配能否让匹配数更大
    
    for(int i=h[t];i!=-1;i=ne[i]){
        int j=e[i];
        
        if(st[j]==0){
            st[j]=1;
            if(match[j]==0||find(match[j])){
                match[j]=t;
                return 1;
            }
        }
        
    }
    return 0;
}


int main(){
    
    cin>>n1>>n2>>m;
    
    memset(h,-1,sizeof h);
    
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    int ans=0;
    for(int i=1;i<=n1;i++){
        memset(st,0,sizeof st);
        if(find(i))
            ans++;
    }
    
    cout<<ans<<endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值