二分图学习笔记

本文仅涉及相关定义与解法,并非附带严格证明,并且定义不为严谨说法,只是为了方便自己理解。

二分图

定义

如果一个图可以把点分为两个集合,使得每一条边两端点分属不同点集,那么这个图就是二分图。

判定

先说一个结论,一个图是二分图等价于一个图不存在奇环,用手画一画很容易得到这个结论。

我们通过染色法来判断一个图是否为二分图。具体流程如下:

我们设dfs(u,c)表示从\(u\)开始染色,\(u\)的颜色染成\(c\)

1.从每个未染色的点开始染色,一般颜色有1,2两种,初始点染色为1,向外拓展所连边。

2.假设当前染色点为\(u\),那么遍历所有\(u\)出边所到达的点\(v\),如果\(v\)未染色,就尝试dfs(v,3-c),如果不合法返回false,合法继续。如果\(v\)已染色,则判断\(v\)颜色是否与\(u\)相同,相同返回false,不同跳过即可。

3.所有\(v\)都合法,返回true

复杂度\(O(n+m\)),\(n\)为点数,\(m\)为边数。

代码实现如下:

bool dfs(int u,int c)
{
    col[u]=c;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(col[v])
        {
            if(col[v]==col[u])
            {
                return false;
            }
        }
        else
        {
            if(!dfs(v,3-c,x))
            {
                return false;
            }
        }
    }
    return true;
}

二分图最大匹配

一些定义

  • 匹配:从一个图里选出一些边,使得每条选出来的边没有公共端点。

  • 匹配边/点:一条边出现在一个匹配的边集里,这条边就称为匹配边,它的两端点都称为匹配点。

  • 非匹配边/点:不是匹配边/点的边/点(多么直白的理解)

  • 增广路:一条连接两个非匹配点且路径上匹配点和非匹配点交替出现的路径。

  • 最大匹配:在所有匹配中,边集大小最大的一种匹配。

求法

二分图最大匹配我们可以使用匈牙利算法(其实好像只是一部分)求解。首先增广路有一个小性质:如果存在一条增广路,那么一定可以多选出一条匹配边(把增广路上的点状态取反)。这个算法的原理也是找增广路。流程如下:

定义Hungary(u)表示从\(u\)开始的一次增广过程,mt[i]表示\(i\)和谁匹配,st[i]表示在本次增广过程中访问到的点。

1.把二分图分为两部分(颜色相同的是一部分),分别称为左部点和右部点。

2.从左部点每个点开始,尝试进行增广,增广过程如下(假设当前访问到点\(u\)):

  • 枚举\(u\)能到达的所有点\(v\)
  • 如果\(v\)已经访问过,跳过,没有,标记已访问,即st[v]=1
  • 如果\(v\)是非匹配点,就把\(v\)匹配给\(u\),即mt[v]=u。如果\(v\)是匹配点,我们就尝试给和\(v\)匹配的点\(mt[v]\)重新找一个匹配点,即Hungary(mt[v]),如果能找到,就把\(v\)匹配给\(u\),否则匹配失败。

3.能匹配的点的个数,就是该二分图的最大匹配。

复杂度\(O(nm)\)\(n\)为点数,\(m\)为边数,但是一般跑不满,实际运行效率挺优秀。

有几个要注意的点:

  • 二分图是无向图,但是我们为了方便,一般只会连左部点到右部点的"有向"边。
  • st数组有必要存在吗?是有的,因为如果一个右部点被多个左部点相连,那么在尝试修改匹配的时候,这个右部点可能会被一直访问,这样就出不来了。
  • 每次清空st数组开销很大,我们可以在Hungary函数中多传一个参数op,表示是第几次增广,这样判断一个点\(v\)是否被访问,只要判断st[v]是否等于op就好了,无需清空。

代码实现如下:

bool Hungary(int u,int op)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(st[v]!=op)
        {
            st[v]=op;
            if(!mt[v]||Hungary(mt[v],op))
            {
                mt[v]=u;
                return true;
            }
        }
    }
    return false;
}

邪门理解

嗯,第一次见到肯定一脸懵逼,神魔增广路,神魔匹配非匹配。我们换一个通俗易懂的解释:

把左部点看作男生,右部点看作女生,之间的边看作男女互相喜欢。

每次尝试给一个男生找女友,当然要找男生女生互相喜欢的,如果女生没有男友,正好两人谈上,有的话,嗯,最邪门的地方来了:让女生尝试把她男友踹掉,让男友找个新女友,如果这个男友找到了新女友,那么皆大欢喜(?),这个女生和来找她的新男生谈上了,如果找不到,这个女生就会可怜这个男生竟然找不到其他女生了,就拒绝这个新男生(?)。如果这个男生被所有女生拒绝,单身一辈子(?),匹配失败,不管了。

如果你想,可以叫做月老算法,当然,这显然叫做NTR算法更合适一点。

二分图最小点覆盖

定义

点覆盖:在图中选出一个点集,使得每一条边都至少有一个端点属于这个点集。

最小点覆盖:最小化这个点集的大小。

求法

直接给结论:二分图最小点覆盖=二分图最大匹配。证明略显复杂,不写了。放一张AcWing算法提高课上y总的证明。

6eca62978e6e97d99112da5e7d94efd8

二分图最大独立集/最大团

定义

独立集:选出一些点,使得这些点之间两两不存在连边。

最大独立集:最大化这些点的个数。

补图:把一张图所有连边的点的边去掉,所有没连边的点连上边,这样得到的一张图就叫做原图的补图。

最大团:补图的最大独立集就是原图的最大团。

求法

结论:二分图最大独立集=点数-二分图最大匹配=点数-二分图最小点覆盖

简单说一下:我们可以把最大独立集看作是"破坏"边的过程,如果一条边有至少一个端点被破坏,那么这条边就被破坏了,这样就转化为了至少选出几个点,能使得所有边至少有一个端点被选,即最小点覆盖,剩下的点就是最大独立集。

有向无环图最小路径点覆盖

定义

最小路径点覆盖:在图中选出最少的不相交路径(即点和边都不重复),使得所有的点都被经过一次。

求法

求有向无环图的最小路径点覆盖用到了拆点的思想,即把每个点\(u\)拆成出入两个部分,为了方便,把左边的点\(u\)称为出点,右边的点\(u'\)称为入点,一条边\((u,v)\),就在新图上转化成了\((u,v')\),可以发现,如果边无向,这就是一张二分图。这有什么用呢?我们来看原图上的一条路径在新图上变成了什么:假设有一条路径\(u->v->p\),那么新图上经过的边就是\((u,v'),(v,p')\),容易发现,因为路径不相交,所以每个点最多经过一次,一条路径,就是一个匹配,一个路径的终点,就是左侧的一个非匹配点。因此,我们现在的问题,转化为了找到一个匹配,使得左侧非匹配点最少,即左侧匹配点最多,也就是求这张新图的最大匹配

有向无环图最小路径可重复点覆盖

定义

最小路径可重复点覆盖:与最小路径点覆盖类似,不过这次可以相交。

传递闭包:一张图上,让一个点\(u\)向所有可以直接或间接到达的点\(v\)(即存在\(u\)\(v\)的一条路径)连边后得到的新图。

求法

我们一样使用拆点法,不过由于路径上点可以重复,所以可能会出现两条路径存在重复点:\((u->y->v)\)\((x->y->z)\),这样,我们如果能让\(x\)点直接到达\(z\),就可以避免重复覆盖\(y\)点了,于是,我们给原图用\(Floyd\)求一个传递闭包,再在传递闭包上进行最小路径点覆盖即为答案。

一些注意

虽然二分图是无向图,但是建图的时候,一般只会让左部点向右部点连一条"有向"边,这是正确的,至于右部点向左部点的连边,我们可以认为是mt数组。

完结撒花!\^_^/

posted @ 2025-11-21 15:45  Traveller1  阅读(0)  评论(0)    收藏  举报