二分图学习笔记

使用题单:二分图 - 从入门到入土

二分图概念

对于一个图,如果能够把它的点集恰好分成两个部分,使得这第一个部分里面的点两两不连边,第二个部分里面的点也两两不连边,则该图是二分图。或者说每一条边都横跨了两个集合。

举个例子:

这个图是二分图,因为我们可以将它分成 { 1 , 3 , 5 , 7 } \{1,3,5,7\} {1,3,5,7} { 2 , 4 , 6 } \{2,4,6\} {2,4,6} 两个点集:

但是这个图:

可以发现这个图无论如何也无法划分点集,所以这个图不是二分图。

还是给一下不说人话的形式化定义:

二分图:如果一张无向图 ( V , E ) (V,E) (V,E) 存在点集 A , B A,B A,B,满足 ∣ A ∣ , ∣ B ∣ ≥ 1 |A|,|B| \ge 1 A,B1 A ∩ B = ∅ A \cap B = \emptyset AB= A ∪ B = V A \cup B = V AB=V,且对于任意 x , y ∈ A x,y \in A x,yA x , y ∈ B x,y \in B x,yB ( x , y ) ∉ E (x,y) \not \in E (x,y)E,则称这张无向图为二分图 A , B A,B A,B 分别称为二分图的左部右部

注意,二分图可以不是连通图。

二分图判定

很容易想到一个小学奥数的知识:**黑白染色。**黑白染色之后若没有冲突的边则就是二分图。

黑白染色就是先任意指定一个点的颜色,然后对于每一条边,都要使两个端点的颜色不同(对应所属于的点集不同),所以每走一条边就变一下颜色。

例如文章自上而下的第一个图黑白染色之后得到:

冲突的意思就是一条边的两个端点的颜色相同。因为在同一条边上面,所以这两个点不能在同一个点集。因为这两个点是因为其他边被迫的颜色相同,所以这两个点又不得不在同一个点集里面(如果不在又会发生其他问题)。所以矛盾,所以有冲突的边的图不是二分图。

别急,我们先不立即学二分图匹配,先来看一道题。

CF862B Mahmoud and Ehab and the bipartiteness

题意就是给定一棵树,求至多添加多少条边使得二分图的性质仍然成立。

显然对于这棵树染色,就可以得出无论加多少边二分图的点集一定是什么样子(如果不是这个样子就违背了最基本的树的条件)。

为了最大化边的数量,一定要让左右部尽可能地交叉连边。算出边的数量之后再减去 n − 1 n-1 n1 即可。

答案: 左部点数量 × 右部点数量 − ( n − 1 ) 左部点数量 \times 右部点数量 - (n-1) 左部点数量×右部点数量(n1)

可以证明,这个值一定不小于 0 0 0。读者可以自己尝试证明一下。

二分图匹配

匹配的定义:在二分图中找到一些边,使得边与边之间没有公共点,那么这组边就是二分图的一种匹配。

最大匹配:边数量最大的匹配。

那么如何求最大匹配呢?这时候就需要请出我们的匈牙利算法(Hungarian Algorithm 了。


匈牙利算法步骤

假设我们现在已经找出了这样的一组匹配:

显然,我们还有一个更加优秀的匹配:

观察上面的三条边,蓝边和红边的关系,我们会发现它们组成了一条路径!

将这个路径弄出来,得到:

黑色边代表还没有被选中的边。而且注意,路径的两头的点一开始都是没有被匹配的,即没有任何一条与其关联的边被选中

是不是可以尝试将选中的边变成没有选中,把没有选中的边变成选中的,从而使答案 + 1 +1 +1

同时,路径的两头的点也匹配到了。

于是,每找到这样的一条路径,我们的答案就可以增加一个。这个结论在下面会证明。

这条路径的名字名扬四海臭名昭著,叫做增广路


所以匈牙利算法本质就是在干找增广路这个过程。

不妨先来看一下它是怎么执行(搜索)的:

还是那张图。

先从 5 5 5 开始,因为 5 5 5 没有对应的点,所以可以选一个。

5 5 5 首先找到了 4 4 4,但是 4 4 4 说它已经有对应点了。

  • 这时候可以放弃,但 5 5 5 就不会有对应点了。
  • 这时候也可以穷追不舍,那得看 3 3 3 的态度:先把 3 3 3 的对应点剥夺了,然后看 3 3 3 能不能找到其他的对应点。

然后 3 3 3 找到了 2 2 2,还要看 1 1 1 能不能找到对应点。

然后 1 1 1 找到了 6 6 6这个时候, 6 6 6 恰好没有对应点,所以 1 1 1 可以和 6 6 6 对应,找到了一条增广路

观察以上的步骤,我们可以发现一个规律:**每一次搜索都是左部点寻找右部点,然后右部点直接去其对应的左部点。**所以在搜索的过程中,还需要同步记录每一个右部点对应的左部点。


匈牙利算法模板

算法的设计是每次都要从一个左部点找增广路,复杂度是 O ( n u m A ∗ m + n u m B ) O(numA * m + numB) O(numAm+numB) 的,其中 n u m A , n u m B numA,numB numA,numB 代表左部、右部的点数, m m m 表示边数。

因为进行了左部点数量次增广路查找,每次都会搜全图,所以是这个复杂度。

其实我看这个复杂度很不顺眼:这么神秘的一番推导咋推出一个平方级的玩意?

当然你要是得出线性做法,这个算法就不叫匈牙利算法了。

另外有一个结论:答案一定不超过 min ⁡ ( n u m A , n u m B ) \min(numA,numB) min(numA,numB)。显然。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N 1000010

int num, res[N], vis[N];
//res[]:右部点的对应点
vector<int> edge[N];

bool match(int u) {
	if (vis[u] == 1)
		return 0;
	else
		vis[u] = 1;
	for (auto v : edge[u])//注意,为了使点的编号不重复,在主函数采用顺序编号,即将右部点的编号全部加上一个 numA
		if (res[v] == 0 || match(res[v]) == 1) { //对方没有对应点,或者对方的对应点可以去找另外一个右部点
			res[v] = u;//增广路上更新匹配
			return 1;
		}
	return 0;
}//O(n + m)

int hungarian() {
	int cnt = 0;
	for (int i = 1; i <= numA; ++i) {
		memset(vis, 0, sizeof(vis));//清空
		cnt += match(i);//如果找到增广路,答案加一
	}
	return cnt;
}//O(numA * m + numB)

匈牙利算法正确性证明

你别看这个算法模板非常的简单,但是这个算法下面的那个函数怎么说都是不对劲的:为什么每一个左部点找一次增广路,就可以得到答案?

我们分类讨论每一个左部点的情况,有以下三种:

  • 这个点已经被匹配到了,不存在增广路。不需要短期内再跑一次增广路。
  • 这个点还没有被匹配,若找到了增广路变成匹配。也不需要短期内再跑一次增广路。
  • 试过了,没有找到增广路,无法匹配。也不需要再跑一次。

这个时候又会有同学问了:现在匹配不了不代表未来匹配不了。

假设在未来形成的增广路的一头是 A A A 点,一头是 B B B 点, A → B A \to B AB 形成了增广路,且在一开始, B B B 没有找到增广路

这时候就需要分类讨论一下。

1.这条增广路中途的选过的边和没有选过的边在中途没有被改过。

那我为什么不能从一开始就从 B → A B \to A BA 走增广路???

然后就有一个发现:增广路的两头是等价的啊!我可以从一端搜到另一端找到增广路,我为什么不能从另一端搜到这一端找到增广路?

于是这个子问题就被解决了。

2.这条增广路中途的选过的边和没有选过的边在中途发生了改变。

那么改变这条增广路径的增广路一定也可以和 B B B 凑成另一条增广路,也是矛盾的。

可以画图理解一下。


所以每一个点找一次增广路,足矣!

然后因为每一个点都会把增广路找尽,所以在算法执行完毕之后不会存在增广路。

那么得出:

  • 如果一个点找到了增广路,那么一定可以对答案构成 1 1 1 的贡献。
  • 如果一个点没有找到增广路,那么这个点这辈子也不会被答案构成贡献了,不需要在之后考虑。

你说得对,我们解决了一个问题,但是正确性还没有完全解决。

接下来还有一个问题:

question:为什么没有增广路的时候,就一定是最大匹配了?

我们可以证明还有增广路的时候,答案就一定不是最大匹配。

但是我们无法根据这个证明没有增广路的时候,答案就一定是最大匹配。

我们使用反证法来解决这个问题:

反证:如果没有增广路,但还是找到了一个更大的匹配,是什么样子的。

再用一下这张图:

不妨再次分类讨论,蓝色边的情况(这里我们只讨论上面的三个蓝色边和两个红色边):

1.某一条蓝色边的两边的点都没有被匹配

**那这条边本身就是增广路啊!**我们可没有规定增广路的长度一定 > 1 >1 >1

那么就矛盾了。

2.每条边的恰好一个点是匹配点

观察到红边一定比蓝色边少,所以还是相连的形式。

那么又出现了增广路,然后又矛盾了。

3.有些边的两个点是匹配点

这个不需要管。可以意会一下。

于是怎么样都是矛盾的,这个问题也被解答了。

P1129 [ZJOI2007] 矩阵游戏

我们来看一道题。

题面:有一个 01 01 01 n × n n\times n n×n 棋盘,可以随意交换任意行和任意列,问主对角线能否全部变成 1 1 1

在这种交换来交换去的题目中,我们需要抓住其中的不变量:同行或同列的点无论经过多少次变换仍然同行或同列。

而主对角线上面有 n n n 个不同行不同列的点,所以我们只需要在原棋盘中找出 n n n 个不同行不同列的 1 1 1 即可。

这时候需要请出经典 trick:把行号作为左半边点,把列号作为右半边点, 1 1 1 的位置看成连接行和列的边。

然后可以直接跑最大匹配,看一下是否 = n =n =n 即可(即找出 n n n 条连接行列的边也就是 1 1 1 的位置,这些边没有公共点也就是不同行不同列)。

因为 n u m A = n u m B = n , m = O ( n 2 ) numA=numB=n,m=O(n^2) numA=numB=n,m=O(n2),所以匈牙利算法的复杂度就是 O ( n 3 ) O(n^3) O(n3),可以通过。

Koenig(柯尼希)定理

首先,我们需要明确一些定义。

最小点覆盖:在二分图中选最少的点,使得每一条边都至少有一个点选中。

柯尼希定理:**二分图的最小点覆盖 = 最大匹配。**就这么一句话,没了。

我们来尝试证明一下。

1.最小点覆盖 ≥ \ge 最大匹配

很显然。

因为最大匹配没有公共点,所以必须每一条边至少选一个点。

2.最小点覆盖 ≤ \le 最大匹配

结论:覆盖点的所有边中,至少有一条的另一端不是覆盖点。这个很显然,可以自己画个图。

把每一个覆盖点到非覆盖点的边选一条,那么一定是匹配的。

反例:如果两个覆盖点的边在一个非覆盖点公共邻居,那么去掉它们选邻居结果更优,就矛盾了。

两个东西结合起来,这个就证明完了。


最大独立集

最大独立集:在图中选出最多的点,使得任意两点之间没有边。

最大独立集 = n n n - 最小点覆盖。

好像最大独立集和最小点覆盖是一个天然互补的关系???好像是真的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值