二分图学习笔记
本文仅涉及相关定义与解法,并非附带严格证明,并且定义不为严谨说法,只是为了方便自己理解。
二分图
定义
如果一个图可以把点分为两个集合,使得每一条边两端点分属不同点集,那么这个图就是二分图。
判定
先说一个结论,一个图是二分图等价于一个图不存在奇环,用手画一画很容易得到这个结论。
我们通过染色法来判断一个图是否为二分图。具体流程如下:
我们设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总的证明。

二分图最大独立集/最大团
定义
独立集:选出一些点,使得这些点之间两两不存在连边。
最大独立集:最大化这些点的个数。
补图:把一张图所有连边的点的边去掉,所有没连边的点连上边,这样得到的一张图就叫做原图的补图。
最大团:补图的最大独立集就是原图的最大团。
求法
结论:二分图最大独立集=点数-二分图最大匹配=点数-二分图最小点覆盖。
简单说一下:我们可以把最大独立集看作是"破坏"边的过程,如果一条边有至少一个端点被破坏,那么这条边就被破坏了,这样就转化为了至少选出几个点,能使得所有边至少有一个端点被选,即最小点覆盖,剩下的点就是最大独立集。
有向无环图最小路径点覆盖
定义
最小路径点覆盖:在图中选出最少的不相交路径(即点和边都不重复),使得所有的点都被经过一次。
求法
求有向无环图的最小路径点覆盖用到了拆点的思想,即把每个点\(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数组。
完结撒花!\^_^/

浙公网安备 33010602011771号