二分图进阶
提醒:二分图问题最重要的还是对问题建模
二分图最大匹配
注:二分图匹配前需要保证点集已经划分为了左右两部分,若未划分,则要先通过二分图染色法作预处理。
匈牙利算法 (Kuhn)
算法流程
模板
const int N = 105;
vector<int> G[N]; // 求解二分图最大匹配时,只需要建左部点 -> 右部点的单向边
int ans; // 最终得到的最大匹配数
int t, vis[N]; // 这里 vis 是 int 类型,使用轮次 t 标记,以避免每次 memset 清空
int match[N]; // match[v] = u,表示右部点 v 与左部点 u 配对
// 匈牙利算法 O(nm)
bool dfs(int u){ // 返回值表示点 u 是否匹配成功
for(auto v : G[u]){
if(vis[v] == t) continue; // 当前轮已经遍历过
vis[v] = t;
if(!match[v] || dfs(match[v])){ // 找到增广路,右部点 v 与左部点 u 可以匹配
match[v] = u; // 当前的未匹配点 v 增加新的匹配,沿途经过的已匹配过的点在回溯过程中更换匹配
return true;
}
}
return false;
}
调用:
t = 1;
for(int i = 1; i <= n; i ++, t ++){ // 对所有左部点均需跑一遍
if(dfs(i)) ans ++;
}
模板理解:二分图内,从左部点 \(\rightarrow\) 右部点的边是非匹配边,通过初始时构建的有向图 \(G\) 表示;而从右部点 \(\rightarrow\) 左部点的边是匹配边,通过数组 \(match\) 映射表示 (\(v \rightarrow match[v]\))
Hopcroft - Karp
也是求二分图最大匹配的算法,复杂度 \(O(\sqrt n * m)\),适用于点数较大的情况,了解即可。
Dinic 算法
二分图可以转换成网络流模型:创建虚拟源点与汇点,将源点连上左边所有点,右边所有点连上汇点,容量均为 1;原二分图内左部点 \(\rightarrow\) 右部点的有向边容量也为 1。
此时最大流即最大匹配。
可以解决的问题
二分图最小点覆盖
在一张无向图中选择最少的结点,满足每条边至少有一个端点被选
结论:二分图中,最小点覆盖中的顶点数量等于最大匹配中边的数量。
具体证明与构造方案见 oi-wiki
二分图最大独立集
在一张无向图中选择最多的结点,满足两两之间互不相邻。
结论:在无向图中,最小点覆盖与最大独立集的大小之和等于顶点数量。
因此,当整个图是二分图时,可以通过求出二分图的最大匹配数,进而得到答案。
有向无环图最小路径覆盖
在一张有向图中,选择最少数量的简单路径,使得所有结点都恰好出现在其中某一条路径。
证明见 oi-wiki
例题
P5182
Q:给定 \(n \times n\) 的棋盘,规定 \(t\) 个给定位置不能放骨牌。求最多能往棋盘上放多少块长度为 \(2\),宽度为 \(1\) 的骨牌。\(n,t \le 100\)
由于同一个格子不能同时被多张骨牌覆盖,将格子看作点,骨牌看作二分图中的匹配边,问题转化为求二分图的最大匹配。
那么二分图中的点应该如何划分成两类呢?每张骨牌覆盖到的两个格子一定相邻,而我们注意到,对于任意两个相邻格子 \((i, j)\),\(i + j\) 的奇偶性一定不同,于是按照格子 \(i + j\) 的奇偶性,将所有点划分成两类;对于每一对相邻格子,若两个格子均可以放置骨牌,则连边,这样就形成了二分图。接下来匈牙利算法跑二分图最大匹配即可。
复杂度分析:点数 \(O(n^{2})\),边数 \(O(n^{2})\),总复杂度 \(O(n^{4})\)。
P3355
Q:给定 \(n \times n\) 的棋盘与 \(m\) 个禁止走到的位置,每个空闲位置可以放中国象棋的棋子 “马”,求使得任意两个马不互相攻击,放置 “马” 的最大数量。
显然会想到将两个可互相攻击到的位置作为二分图连边,问题等价于求解二分图的最大独立集大小,但问题在于如何将棋盘的所有空闲位置划分成两类,形成二分图。我们会发现,将棋盘的所有位置黑白交替染色,存在性质:任意两个格子颜色是否相同取决于二者曼哈顿距离的奇偶性。而对于任意两个可互相攻击的格子,曼哈顿距离固定为 \(3\),因此将棋盘按照黑白交替染色,恰好可以保证图中的任意边连接的两个结点颜色不同,进而保证原图是二分图。
CF1404E
与上一题类似:棋盘大小改为 \(n \times m\)(\(n,m \le 200\)),骨牌大小为 \(1 \times x\) 或 \(x \times 1\)(\(x\) 可以为任意正整数),某些给定位置的 \(1 \times 1\) 方格不能被覆盖,求将所有空闲位置全部覆盖,需要的最少骨牌数量。
先将所有空闲位置填充一块 \(1 \times 1\) 骨牌,然后尝试消除尽可能多的交界位置(两侧均为空闲方格的交界处)。
图论建模:将所有可消除的交界位置看作结点,将任意一对在同一个 \(1 \times 1\) 方格内,且共享方格顶点的两个交界处代表结点连边。如下图所示:
显然,我们选取的边界集合必须满足在图中任意两点之间无连边,也就是选取最大独立集。而无向图的最大独立集是一个 NP 问题,但在二分图中可以通过最大匹配间接求得。比较幸运的是,我们刚刚建的图恰好符合二分图性质:只有横向边界与纵向边界之间才有连边,因此可以按照横向与纵向将边界分成两类。于是,建图后跑二分图最大匹配即可。
需要注意的是,总点数与总边数均为 \(O(nm)\),若使用 Kuhn 算法会超时,因此要使用优化算法(Hopcroft - Karp 或 Dinic),复杂度 \(O(nm \sqrt{nm})\)。
P2071
Q:已知车上有 \(N\) 排座位,有 \(2N\) 个人参加省赛,每排座位只能坐两人,且每个人都有自己想坐的排数(每个人恰好 \(2\) 排),问最多使多少人坐到自己想坐的位置。
由于每一排可以容纳两个人,将每一排用两个不同编号的结点表示,然后按照需求关系将人与排之间连边,跑二分图最大匹配即可。
U455932
Q:给定两台机器 \(A,B\),其中机器 \(A\) 有 \(N\) 个模式(\(0 \backsim N - 1\)),机器 \(B\) 有 \(M\) 个模式(\(0 \backsim M - 1\)),初始时机器 \(A,B\) 均处于模式 \(0\);有 \(K\) 个任务,第 \(i\) 个任务必须要么在机器 \(A\) 以模式 \(a_{i}\) 运行,要么在机器 \(B\) 以模式 \(b_{i}\) 运行。可以随意决定所有任务的执行顺序,但是每台机器转换一次模式就需要花费一个代价,求执行完所有任务的最小总代价。
将模式看作点(注意区分不同机器同一编号的模式),任务看作边。显然同一模式需要尽量连续执行,以避免花费代价,问题转化为了求解二分图的最小点覆盖(即选择最少的模式,使得所有任务都能被完成)。特别注意初始时两台机器模式均为 \(0\),若某个任务在某台机器需要以模式 \(0\) 完成,则可以直接忽略。
P6062
P1129
Q:给定一个 01 矩阵,每次可以交换任意两行或任意两列,问是否可以通过任意次操作,让主对角线上的值均为 \(1\)。\(n \le 50\)
考虑题述操作,会发现一个重要性质:某一行内的所有元素集合均不会变动(交换两行只是整体位置发生变化,交换两列只是内部顺序发生变化),转化到列上同理。
考虑从最终形态倒推,可以发现条件成立当且仅当 \(n\) 个点在整个二维图内形成错位排列(也就是存在 \(n\) 个点,它们的横坐标构成行的全排列,纵坐标构成列的全排列)。
这也就意味着对于任意一行,都能通过某个 \(1\) 匹配到 唯一的 某一列(可以理解成行编号与列编号一一匹配,构成排列映射)。于是考虑用二分图建模该问题:将所有行编号看作二分图的左部点,所有列编号看作二分图的右部点,图中每个 \(1\) 的坐标 \((x, y)\) 看作左边的 \(x\) 连向右边的 \(y\)。则上述条件成立等价于二分图的最大匹配数为 \(n\)。由于 \(n\) 很小,跑匈牙利算法即可。
复杂度分析:点数 \(O(n)\),边数 \(O(n^{2})\),总复杂度 \(O(n^{3})\)。
Gym 104427B
Q:\(n\) 名律师,给出 \(m\) 对辩护关系 \((a, b)\),表示 \(a\) 可以为 \(b\) 辩护。每名律师可以为多名其他律师辩护,但是不能出现某两名律师互相辩护的情况(若 \(a\) 为 \(b\) 辩护,则 \(b\) 不能再为 \(a\) 辩护)。求是否存在一种方案,使得所有律师均可以被辩护至少一次。
图论建模:可以看作无序对 \((a, b)\) 只能被 \(a\) 与 \(b\) 最多一个占用辩护权(被 \(a\) 占用相当于 \(b\) 为 \(a\) 辩护,被 \(b\) 占用相当于 \(a\) 为 \(b\) 辩护),而辩护关系数量是有限的,故我们考虑将每个辩护关系看作结点(左部点),去匹配被辩护律师代表的结点(右部点),每个辩护关系 \((a, b)\) 只可以与 \(a,b\) 两者中的恰好一个匹配,问题便转化为了求二分图最大匹配数是否等于 \(n\)。由于 \(n,m \le 2 \times 10^{5}\),Dinic 求解即可。
CF1139E
Q:给定 \(m\) 个共有 \(n\) 个元素的可重集(总元素数为 \(O(n)\),分布任意),一共 \(q\) 次操作,每一次从某一个可重集里面删除某个指定元素,然后查询在每一个可重集里选至多一个元素,组成集合可以达到的最大 \(mex\)。\(m \le n \le 5000\)
先不考虑删除操作,思考该怎么做。显然对于每种数,最多只会从一个多重集中取,故我们可以考虑建立多重集与元素之间的匹配关系。
图论建模:将每个可能取出的元素(\(0 \backsim m\))看作左部点,每个多重集 \((1 \backsim m)\) 看作右部点,将多重集与其内部包含的所有元素之间各连一条有向边。
由于需要最大化 \(mex\),我们每次贪心匹配左部点内的 \(mex\),匹配成功则扩大 \(mex\) 继续匹配即可。
现在相当于要维护每次删除一条边后的最大匹配情况。对于二分图,删除某一条边会引起匹配情况的巨变,删边前的匹配将不可复用;而 假设每次操作是加入一条边,则加边之前的匹配仍可复用,只需要接着未匹配的左部点开始继续增广即可。故我们考虑倒序处理操作,问题等价于维护每次加入一条边后维护新的最大匹配。
复杂度分析:点数 \(O(m)\),边数 \(O(n)\);每次操作时执行匈牙利算法会成功若干次(\(\ge 0\))与失败一次,而总成功次数最多为 \(m\),可知复杂度为 \(O(q * n + m * n)\)(失败 + 成功)。
综上,总复杂度为 \(O(q * n + m * n)\)。






浙公网安备 33010602011771号