二分图——匈牙利算法——的学习

一、什么是二分图:

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V,E 可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

二分图

二、二分图的判断、二分图染色

判断方法就是染成两种颜色。

如果与相连切未染色 就把他染成异色
如果相连的已经染色 就判断是否颜色不同 (如果 颜色相同 表示不能构成二分图)

注意!!!:不连通的图(有多个连通分支的图)!

①bfs 判断:

int vis[maxn]; /*********染色标记数组******/
int bfs(int s)
{
    memset(vis,0, sizeof(vis));/******/
    queue<int>que;
    que.push(s);
    vis[s] = 1;
    while(!que.empty())
    {
        int tp = que.front();
        que.pop();

        for(int i = 1;i <= M;i++)
        {
            if(mp[tp][i])/*****如果相连接**/
            {
                if(!vis[i])/***没被遍历, 就 染成 与tp 不同颜色的图*****/
                {
                    vis[i] = vis[tp]== 1?2:1;
                    que.push(i);
                }
                else if(vis[i] == vis[tp])/*****如果与自己相邻 且颜色相同 表示不是二分图 返回1******/
                return 1;
            }
        }
    }
    return 0;
}

② dfs判断:


#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
 
const int MAX_N =105;
int V,E;
// 使用邻接表模拟一张无向图
vector<int> G[MAX_N];
// 顶点的颜色,初始化为0,上色有两种颜色(0 or 1)
int color[MAX_N];
 
bool dfs(int v, int c)
{
    color[v] = c;       // 把顶点染成c
    for(int i = 0; i < G[v].size(); i++)
    {
        // 如果当前点的相邻的点同色就返回false
        if(color[G[v][i]] == c)
            return false;
        // 如果当前点的邻点还没被染色,就染成-c
        if(color[G[v][i]] == 0 && !dfs(G[v][i], -c))
            return false;
    }
    // 如果当前点都被染过色,就返回true
    return true;

三、匈牙利算法的关键:

①有一个可以储存所有二分图连线的数组(可以是一维,也可以是二维数组,怎么方便怎么来 )
②有一个link[]数组(名字叫 连接数组),用于标记已经链接的 二分图定点。
③就是有一个mark[] (标记数组)——作用:在拆分某个两个顶点时, 防止这两个点在连接

大牛的匈牙利算法的讲解(通俗易懂):

https://blog.csdn.net/dark_scope/article/details/8880547

三、相关题:

二分图——匈牙利算法的匹配题:
  1. hdu1083(二分图匹配):https://cn.vjudge.net/problem/HDU-1083
  2. HDU1083:https://vjudge.net/problem/HDU-2063
HDU1083 过山车题
#include<stdio.h>
#include<string.h>
int map[505][505];// i表示男  j表示女   i男和j女可以作伴 就 在i j 处标记 (即 储存 输入的信息)
int link[505];//用于标记 已经配对完成的 男女(连接函数)
int mark[505];//用于标记 该男子不能再配对的 女

int K, M, N;
int dfs(int x)
{
    int j;
    for(j = 1; j<= M; j++)
    {
        if(map[x][j] == 1&&mark[j] == 0)// 如果该男子和女子可以作伴, 并且mark为0, 就可以进行内部操作
        {

            mark[j] = 1;//标记
            if(link[j] == 0||dfs(link[j]))//如果 该女子和别的男人已经作伴, 就拆散他们 让那个男的在找一个(因为mark 已经标记 所以那个男的不可以在找该女子)
            {
                link[j] = x;//现在的男子和女子作伴
                return 1;//返回一表示 又有一对
            }



        }

    }
    return 0;
}

int hunger()
{
    memset(link, 0,sizeof(link));//先初始化 连接函数
    int res = 0, i;//res 统计最多有几对
    for(i = 1; i <= N; i++)
    {
        memset(mark, 0,sizeof(mark));//初始化标记函数,    为0表示 在i男子若与某女子可以作伴的前提下,  该男子可以和女子连接
        res+=dfs(i);//

    }
    return res;
}

int main ()
{


    while(~scanf("%d", &K)&&K!= 0)
    {
        scanf("%d %d", &M, &N);
        int i;
        memset(map, 0, sizeof(map));
        for(i = 0; i < K; i++)
        {
            int a,b;
            scanf("%d %d", &a, &b);
            map[b][a] = 1;
        }

        int sum = hunger();
        printf("%d\n", sum);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值