二分图及其应用

二分图

本博客由南昌理工学院ACM集训队独家赞助播出

什么是二分图

二分图,简单来说就是一个所有的顶点可以分为两个子集(部分),并且每个子集内部的顶点互不相连的图

人们经常将二分图两个集合的比作男性和女性,
并将两个集合间的连线比作通常意义下的男女间的–好感关系–。
(在这个图中)男男不能搞基,女女不能搞百合 < 也就是每个集合内部顶点不相连 >.(ps:对同性恋不抱反对态度,我本人非常支持,只是就这个例子而言,如有冒犯,当我没说)

如图
在这里插入图片描述

  • 子集u 和 子集v 相连(黑点和蓝点相连)
  • 同子集点并不相连(黑点不连黑点,蓝点不连蓝点)
  • 区别二分图,关键是看点集是否能分成两个独立的点集

二分图的性质

有关二分图的名词:
在这里插入图片描述

  1. 匹配点、非匹配点:两个独立集中被选择的相匹配的两个点,称为匹配点,其他则为非匹配点,例如上图中的1,6,4,7为匹配点,其他点为则为非匹配点。
  2. 匹配边、非匹配边:一张图中的两个独立集有一些匹配关系,所选择的匹配关系,则称为匹配边,未被选择的则称为非匹配边。例如上图中的1~6, 4~7 为匹配边,其余边为非匹配边。
  3. 交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径称交替路。 例如图中,从2出发的一条路径,2~ 7 ~ 4 ~ 8则为一条交替路
  4. 增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路。增广路的一个重要特点:非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配!!!。例如图中,那一条交替路,2 ~ 7 有一条连线, 但不幸 7 ~ 4 已经相连 ,再看原来还有可选择的另一个未被连接的点 8 所以可将 4 ~ 8相连,并将 7 让给 4, 多增加一条匹配边。
  5. 最大匹配:两个独立的集合顶点间有一些连线,让你选择一些连线,使得每个顶点最多只能与另一集合的唯一一个顶点相连的最多条数,也就是最大匹配数。 用上述的男女好感关系例子来说,就是为有好感的男女牵红线,你所能促成的最多的情侣对数(一个男生只能同时与一个女生相恋,没有恶俗的三角恋,不过可能有单身狗qwq)。通常会采用匈牙利算法来解决此类问题,稍后会做介绍。
  6. 完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。用上述的例子来说就是:能否将互有好感的男女两两配对,使得每对都互有好感?
  7. 最小点覆盖数:求最少的点使得每条边都至少和其中的一个点相关联,很显然直接取最大匹配的一段节点即可。最大匹配数=最小点覆盖数
  8. 最大独立数:二分图的独立数等于顶点数减去最大匹配数。很显然的把最大匹配两端的点都从顶点集中去掉这个时候剩余的点是最大独立集。
  • 图内顶点分为两个独立的子集,同子集内部点互不相连(充要条件
  • 任何无回路的图都是二分图

染色法判定二分图

如何判定一个图是否为二分图呢?
已知二分图内的顶点是两个独立的子集,同子集内部点互不相连。
所以可以通过将两个集合点染上不同颜色,来判断是否与定义矛盾,若矛盾则不为二分图,反之,则得证该图为二分图。
代码模板如下:

const int m=1e5;
int n; //总点数
int h[N], e[M], ne[M], idx; //邻接表存储图
int color[N];// 颜色,-1未染色,0表示白色,1表示黑色

//u表示当前节点,c表示当前点的颜色
bool dfs(int u, int c)
{
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (color[j] == -1){
            if (!dfs(j, !c)) return false;
        }
        else if (color[j] == c) return false;
    }
    return true;
}
bool check()
{
    memset(color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!dfs(i, 0)) {
                flag = false;
                break;
            }
    return flag;
}

最大匹配(匈牙利算法)

匈牙利算法多用于求解最大匹配数,在上述解释名词定义时,提到的增广路的概念,正是匈牙利算法的做法。找到一条增广路,则可改进匹配,更改匹配状态,使匹配数+1, 当找不到一条增广路时,则达到最大匹配。
它的操作步骤可简述为:

peo[i] 存放与顶点 i 匹配的顶点标号

  • 依次遍历n个顶点中每个顶点所连的边
  • 当前顶点A与 顶点B、D、E 相连, 从前向后依次遍历所连顶点,所连的顶点B未被匹配,则标记与B匹配的点为A,即peo[B]=A,返回匹配成功,并跳出循环执行下一顶点。
  • 下一顶点C与B相连查看 (与B匹配的点peo[B]->) A是否还有其他相连的未匹配点已知A也与D相连,则标记与D匹配点为A,peo[D]=A, 并将顶点B让给顶点C , peo[B]=C

代码模板:

int n;//顶点数
int peo[205]; //匹配数组
int st[205];  //标记询问数组
vector<int>v[205];
bool dfs(int u)//u表示当前传入的顶点
{
    int len=v[u].size(); 
    for(int i=0; i<len; i++)
    {
        int j=v[u][i]; //与当前顶点u 相连的顶点 j 
        if(!st[j]) //如果当前顶点未被询问
        {
            st[j]=1; //标记询问
            if(peo[j]==0||dfs(peo[j]))
            {//如果顶点j未被匹配 或者 与j匹配的顶点有其他选择
                peo[j]=u; //标记与j匹配点为u
                return true; //返回匹配成功
            }
        }
    }
    return false; //若未成功返回,则匹配失败
}

int maxp()
{
	for(int i=1; i<=n; i++)
    {
        memset(st,0,sizeof(st)); //清空标记数组
        if(dfs(i)) ans++; 如果匹配成功 匹配数+1
    }
}

推荐例题:

二分图题目难度在于如何建图,很多时候看不出它是一道二分图,如果换个角度看就会豁然开朗,还是需要多做题的,共勉。

luogu : P3386 【模板】二分图最大匹配

AcWing : 1394. 完美牛棚

Acwing : 372. 棋盘覆盖

Acwing : 376. 机器任务

Acwing : 378. 骑士放置

Acwing : 379. 捉迷藏

Acwing : 2199. 骑士共存问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值