自学内容网 自学内容网

二分图算法总结 C++实现

总体概念

图源ACWING

染色法

基本思路步骤

  1. 将所有的边及其相接的边用邻接表存储起来;
  2. 遍历所有的点,找到未上色的点;
  3. 用BFS将该点及其相接的点迭代上色;
  4. 在上述染色步骤中,如果相邻点的颜色相同则无法形成二分图;

题目

图源ACWING

题解

在这里插入图片描述
原链接:https://www.acwing.com/solution/content/105874/

代码实现

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

using namespace std;

const int N = 100010 * 2;//因为是无向图,所以每两个点之间的边要存储两次
int e[N], ne[N], idx;//邻接表存储图
int h[N];
int color[N];//保存各个点的颜色
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 染色

    //遍历和 u 相邻的点
    for(int i = h[u]; i!= -1; i = ne[i])
    {
        int j = e[i];                   
        if(!color[j])//相邻的点没有颜色,则递归处理这个相邻点
        {
            if(!dfs(b, 3 - c)) return false;//(3 - 1 = 2, 如果 u 的颜色是2,则和 u 相邻的染成 1)
                                            //(3 - 2 = 1, 如果 u 的颜色是1,则和 u 相邻的染成 2)
        }
        else(color[j] == c)//如果已经染色,判断颜色是否为c
        {                                     
            return false;//如果是,说明冲突,返回                   
        }
    }
    return true;
}

int main()
{
    memset(h, -1, sizeof h);//初始化邻接表
    cin >> n >> m;
    for(int i = 1; i <= m; i ++)//读入边
    {
        int a, b;
       scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    for(int i = 1; i <= n; i ++)//遍历点
    {
        if(!color[i])//如果没染色
        {
            if(!dfs(i, 1))//染色该点,并递归处理和它相邻的点
            {
                cout << "No" << endl;//出现矛盾,输出NO 
                return 0;
            }

        }
    }
    cout << "Yes" << endl;//全部染色完成,没有矛盾,输出YES
    return 0;
}

作者:Hasity
链接:https://www.acwing.com/solution/content/105874/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

匈牙利算法

基本思路步骤

  1. 邻接表存储边;
  2. 遍历左侧的点,接着遍历与其相连的在右侧点(二分图点被划分为左右两侧);
  3. 找到未连过其他点的点与其相连;
  4. 如果该点已经连接,尝试让连接这个点的左侧的点重新连接其他点;

题目

图源ACWING

具体题解

在这里插入图片描述
原链接:https://www.acwing.com/solution/content/179030/

代码实现

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

using namespace std;

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

int h[N], e[M], ne[M], idx;//邻接表储存边
int match[N];//match[i] = j表示与点i相连的点是j;是记录整个过程配对情况的数组;
bool st[N];//遍历到左侧一个点时,判断右侧的点是否被该点访问过了;是记录一次循环的访问情况的数组;
int n1, n2, m;
int res;//结果集

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])//遍历与点x相连的点
    {
        int j = e[i];
        if(!st[j])//如果没有被x访问过
        {
            st[j] = true;//将其标记为true,防止重复访问;
            if(match[j] == 0 || find(match[j]))//如果该点没有与其他点配对过 或者 与该点配对的点能找到其他的点与其配对;
            {
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    cin >> n1 >> n2 >> m;
    
    memset(h, -1, sizeof h);
    
    int a, b;
    
    while(m -- )
    {
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    
    for(int i = 1;i <= n1;i ++ )
    {
        memset(st, false, sizeof st);//每次访问右侧的点时,需将st全赋值为false防止被上次循环的find影响本次find的访问;
        
        if(find(i))
        {
            res ++ ;
        }
    }
    
    cout << res;
    
    return 0;
}

原文地址:https://blog.csdn.net/m0_62112384/article/details/142719110

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!