二分图ovo

本文介绍了图论中的二分图概念,即如何将图中的点分为两组,使得每条边连接不同组的点。同时讲解了判断二分图的染色法和求解二分图最大匹配的匈牙利算法。提供了相应的C++代码实现,帮助理解这两种算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【概念】 

在一张图中,如果能够把全部的点分到两个集合中,保证两个集合内部没有 任何边 ,图中的边只存在于两个集合之间,这张图就是二分图。

换句话说,二分图中不存在连接同一集合的不同点的边。

Forexample:

 

【常用算法】

1.染色法(判断一个图是否为二分图)

2.匈牙利算法(求二分图的最大匹配数)

模板题1 染色法判定二分图

【分析】

1.首先,对任意一个没有被染色的点染色。

2.然后判断其相邻的顶点是否染色。

如果没有,则染上相反的颜色;

如果已经染色且与原点的颜色相同,说明与二分图的定义存在矛盾,则该图不是二分图。

3.以上过程可以用bfs或dfs实现(染色默认染成1和2)。

Attention:无法使用两种颜色为奇数顶点个数的图染色。

【代码】

#include <bits/stdc++.h>
using namespace std;
const int N=100010,M=200010;
int h[N],e[M],ne[M],idx;
int n,m;
int color[N];
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool dfs(int x,int c){
    color[x]=c;//将x点染成c颜色
    for(int i=h[x];i!=-1;i=ne[i]){//遍历相邻的点
        int j=e[i];
        if(!color[j]){//没有被染色
            if(!dfs(j,3-c)) return false;//将其染色并递归处理相邻点
        }
        else if(color[j]==c) return false;//一条边的两端点存在于同一个集合
    }
    return true;
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof(h));
    while(m--){
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }//无向图
    bool flag=true;
    for(int i=1;i<=n;i++){
        if(!color[i]){//没有被染色
            if(!dfs(i,1)){//将其染色
                flag=false;
            }
        }
    }
    if(flag) puts("Yes");
    else puts("No");
}

模板题2 二分图的最大匹配 

【小知识】

匹配:在图论中,一个 “ 匹配 ” 是一个边的集合,其中任意两条边都没有公共顶点。

最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。

完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。

交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。

增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替 路称为增广路(agumenting path)。

匈牙利算法(Hungarian algorithm):图论中寻找最大匹配的算法,暂不考虑加权的最大匹配

 

【分析】

1.存图模板

int h[N],ne[N],e[N],idx;
//n1,n2分别是两个点集的点的个数
int n1,n2,m;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

//存边只存一边就行了,虽然是无向图,但是只需要遍历一边。
for(int i=0 ; i<n1;i++){
    int a,b;
    cin>>a>>b;
    add(a,b);
}

2.一个找对象的过程 

If the girl you want already has a boyfirend,then ask her boyfirend whether he could find another girl and leave this one for you !

TIP: 因为你要去问的都是男孩子,所以存边的时候,都是由男孩子指向女孩子

 

【代码】

#include <bits/stdc++.h>
using namespace std;
const int N=510,M=1e5+10;
int h[N],e[M],ne[M],idx;
bool st[N];
int match[N];//某点对应的boyfriend
int n1,n2,m;
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool find(int x){
    for(int i=h[x];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            st[j]=true;
            if(match[j]==0||find(match[j])){//还没有boyfriend或者boyfriend有second choice
                match[j]=x;
                return true;//脱单
            }
        }
    }
    return false;
}
int main(){
    memset(h,-1,sizeof h);
    cin>>n1>>n2>>m;
    while(m--){
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    int res=0;//最大匹配数
    for(int i=1;i<=n1;i++){
        memset(st,false,sizeof st);//初始化每一个都没有
        if(find(i)) res++;
    }
    cout<<res;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值