图论:二分图问题

前言:今天来记图论中二分图的问题,二分图会是很多图论中的盲点,这里记两个关于二分图的算法

目录

1、染色法判段二分图

2、匈牙利算法


1、染色法判段二分图

思路:染色法的核心思想为将当前节点染成一个颜色,然后将他连接的边全部染成另一个颜色。

如果节点中存在奇数环,就会存在冲突,即要染成不同颜色的点和当前节点相同颜色。

代码模板:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10,M=2e5+10;
int h[N],e[M],ne[M],idx;//数组模拟链表
int color[N];//当前节点颜色,0代表未染色
int n,m;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;//数组模拟链表插入操作
}
bool dfs(int u,int c){
    color[u]=c;//将u节点染成c色
    for(int i=h[u];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(){
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);//头结点初始化为-1
    while(m--){
        int a,b;
        scanf("%d%d",&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;
                break;
            } 
        }
    }
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

2、匈牙利算法

题目模板:给定一个二分图,n1代表左边节点,n2代表右边的结点,m为边数,求左边和右边的最大匹配数。

思路:匈牙利算法的思路就很有意思了,如果左边的a和右边的m结点匹配了,a还能和右边的n匹配,遍历到下一个结点b的时候,发现b只能和m匹配,b就去问a,a能不能把m让给他,和n匹配,多么像绿帽子算法,好一个资源最大化!

上代码!

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,M=1e5+10;

int h[N],e[M],ne[M],idx;//数组模拟链表
int match[N];//当前点的匹配对象
bool st[N];
int n1,n2,m;//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]||find(match[j])){
                match[j]=x;
                return true;
            }
        }
    }
    return false;
}
int main(){
    scanf("%d%d%d",&n1,&n2,&m);
    memset(h,-1,sizeof h);
    while(m--){
        int a, b;
        scanf("%d%d",&a,&b);
        add(a,b);//因为这里只从n1往n2走,所以不用记反向
    }
    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、付费专栏及课程。

余额充值