【学习笔记】二分图
感觉需要恶补一下二分图。
二分图的概念
对于一个无向图,如果其点集能被分为两部分,使得同一部内没有连边,那么这个图就是一个二分图。
二分图有几个基本的性质:
- 一个二分图可以被黑白染色,并且对其进行黑白染色后,同一部的颜色相同、
- 从二分图的某一个点开始 BFS,那么所在的奇数层和偶数层分别为左部和右部的点。
- 一个二分图的每一个环的长度都是偶数。
二分图的匹配
对于一个二分图,若它的边集的一个子集满足每两条边没有公共端点,那么称这个边集为一个匹配。
很多问题都可以转化为二分图的匹配问题,比如变量与值的匹配。
匈牙利算法
匈牙利算法的核心思想就是每一次从左部的一个点开始,寻找一个增广路,满足这条路径上匹配的边和未匹配的边交替出现,最后以未匹配的边结束。这条路的长度为奇数,然后将这条增广路上的边全部翻转,这样每一次就会使得匹配的边集扩大
bool match(int i) {
if (vis[i]) return false;
vis[i] = 1;
for (int j : e[i]) if (!p[j] || match(p[j])) {
p[j] = i;
return true;
}
return false;
}
int hungarian() {
int cnt = 0;
for (int i = 1; i <= n; i++) {
memset(vis, 0, sizeof vis);
if (match(i)) cnt++;
}
return cnt;
}
一共 DFS
网络流做法
考虑直接建网络流,源点连左部,右部连汇点,直接跑就行。
考虑每次 BFS+DFS 是
最大权匹配
直接跑费用流吧
upd. 我错了,别跑费用流吧
KM 算法:求最大权完美匹配。如果两边点数不一样就补一下让两边一样。
考虑给每个点赋一个权值
我们定义相等子图为,将所有满足
那么考虑类似于匈牙利的做法,每次考虑找到一个点在相等子图上的匹配,如果能增广成功则结束,否则考虑调整当前的可行顶标使得更多的边加入相等子图,进而存在增广路让匹配增加。考虑交错树(即匈牙利遍历到的所有点形成的树)上的点,记树上的左部点集合为
考虑将
每个点可能会进行
const long long INF = 0x3f3f3f3f3f3f3f3f;
long long c[MAXN][MAXN], lx[MAXN], ly[MAXN], slk[MAXN];
int match[MAXN];
bool visx[MAXN], visy[MAXN];
bool dfs(int u) {
visx[u] = 1;
for (int v = 1; v <= n; v++) if (!visy[v]) {
long long t = lx[u] + ly[v] - c[u][v];
if (t) {
slk[v] = min(slk[v], t);
} else {
visy[v] = 1;
if (!match[v] || dfs(match[v])) { match[v] = u; return true; }
}
}
return false;
}
long long km() {
for (int i = 1; i <= n; i++) lx[i] = ly[i] = match[i] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) lx[i] = max(lx[i], c[i][j]);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) slk[j] = INF;
while (1) {
for (int j = 1; j <= n; j++) visx[j] = visy[j] = 0;
if (dfs(i)) break;
long long d = INF;
for (int j = 1; j <= n; j++) if (!visy[j]) d = min(d, slk[j]);
for (int j = 1; j <= n; j++) if (visx[j]) lx[j] -= d;
for (int j = 1; j <= n; j++) if (visy[j]) ly[j] += d; else slk[j] -= d;
}
}
long long ans = 0;
for (int i = 1; i <= n; i++) ans += c[match[i]][i];
return ans;
}
注意到问题在于每次重新匹配太浪费时间,所以可以复用上一次的匹配结果,从加入的边开始进行增广。额我真看不下去了先咕了,如果真碰到题卡我这个了再回来学吧。
最大匹配相关的转化
最小点覆盖 = 最大匹配
最大独立集 =
完美匹配
完美匹配即匹配的数量等于左部点的数量的匹配。
Hall 定理
一个二分图存在完美匹配的充要条件为:对于左部点的任意一个子集,其相连的右部的点集大小都大于等于这个子集的大小。
例子:
,并且 ,求是否存在一组 的解,使得 互不相同。 将左侧
向右侧 连边,就变成了是否存在完美匹配的问题。 考虑右部的点的集合,如果为几段不连续的区间,那么我们可以将它划分为几个不相交的子问题。
所以我们只考虑连续的区间,那么只需要满足左侧点的数量小于等于右侧点的数量,于是可以推出
。 证明:如果
是原来的一个区间,那么这个式子肯定没问题,如果不是原来的一个区间,那么必定会存在一个 且 ,这时候已知 对于上式是成立的,那么 也一定成立。
二分图匹配的可行边和必须边
必须边的定义:二分图的任意一个最大匹配都包含的边。
可行边的定义:至少属于一个二分图的最大匹配的边。
首先对二分图跑一遍网络流。
必须边的判定条件:边的流量为
可行边的判定条件:边的流量为
这个东西与网络流中最小割的可行边和必须边是等价的。P4126
网格图模型
对网格图进行黑白染色,则图上相邻的两个点必定为一个黑点一个白点。P5038
P5030:这题比较有意思,可以发现能互相攻击到的点必定在同一种颜色的格子上,且横坐标的奇偶性一定不同,于是可以根据奇偶性来划分左部和右部,然后就是二分图最大匹配问题了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】