二分图算法总结


二分图概念: 设 G=(V,E) 是一个无向图,如果顶点集合 V 可分割为两个互不相交的子集 (A,B),并且每条边 (i,j)∈E 的两个端点 i 和 j 分别属于这两个不同的顶点子集,则称图 G 为一个二分图。
二分图判定: 如果图中不存在奇数环,则这个图是二分图

1. 二分图染色法

在这里插入图片描述

输入:
4 4
1 3
1 4
2 3
2 4
输出:
Yes
#include<iostream>//染色法即将相邻的两个点赋成不同的值,若在赋值的过程中出现相邻的两个顶点值相同,则不是二分图
#include<vector>
using namespace std;

int n,m;
vector<int> v[100010];
int color[100010];

bool dfs(int u,int c)
{
    color[u]=c;
    for(int i=0;i<v[u].size();i++)
    {
        int j=v[u][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;
    int a,b;
    while(m--)
    {
        scanf("%d %d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    bool flag=true;
    for(int i=1;i<=n;i++)
    {
        if(!color[i])
        {
            if(!dfs(i,1))
            {
                flag=false;
                break;
            }
        }
    }
    if(flag) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

2. 树与二分图

在这里插入图片描述

输入:
7
1 2
2 3
2 4
2 5
2 6
4 7
输出:
4
#include<iostream>
#include<vector>
#include<stdio.h>
using namespace std;

int n;
long long ji,ou;//根据二分图的判定性质,只有树的奇数层上的点和偶层上的点链接才能构成偶数环,而构成偶数环,才能保证是二分图
vector<int> v[1000010];
bool st[1000010];

void dfs(int u,int dep)
{
    st[u]=true;
    if(dep%2==1) ji++;
    else ou++;
    for(int i=0;i<v[u].size();i++)
    {
        if(!st[v[u][i]]) dfs(v[u][i],dep+1);
    }
}

int main()
{
    cin>>n;
    int a,b;
    for(int i=0;i<n-1;i++)
    {
        scanf("%d %d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    dfs(1,1);//第一个值传的是节点的编号,理论上讲,这里传任何一个合法的节点都可以,因为1在任何时候都合法,因此方便起见,这里传1
    cout<<ji*ou-(n-1)<<endl;//减去树中已存在的n-1条边,这些边恰好都在乘机里,因为他们互为奇层偶层的关系
    return 0;
}

3. 二分图的最大匹配(顶点之间的两两配对)

在这里插入图片描述

输入:
2 2 4
1 1
1 2
2 1
2 2
输出
2
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;

int n1,n2,m;
int res;
vector<int> v[510];
bool st[510];//右边是否被访问过
int match[510];//下标为右边,值为左边

bool find(int x)
{
    for(int i=0;i<v[x].size();i++)
    {
        int j=v[x][i];
        if(!st[j])//在回溯的过程中,这里挡住了已经找过的门槛
        {
            st[j]=true;
            if(match[j]==0||find(match[j]))//左边条件好理解,j没有被匹配过,直接匹配成功;右边条件是被匹配过的情况,这时候需要回溯,看左边这个要匹配的还有没有其他的人选,一层一层的往上寻求答案
            {
                match[j]=x;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    cin>>n1>>n2>>m;
    while(m--)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        v[a].push_back(b);//这里只需要左边指向右边即可
    }
    for(int i=1;i<=n1;i++)
    {
        memset(st,false,sizeof st);
        if(find(i)) res++;
    }
    cout<<res<<endl;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勇敢nn

心动不如行动,感谢您的支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值