二分图总结
匈牙利算法
基于贪心的思想。
-
首先从任意一个未配对的点 (x) 开始,选择他的任意一条边((x) - (y) ),如此时 (y) 还未配对,则配对成功,配对数加一,若 (y) 已经配对,则尝试寻找 的配对的另一个配对(该步骤可能会被递归的被执行多次),若该尝试成功,则配对成功,配对数加一。
-
若果上一步配对不成功,那么选择重新选择一条未被选择过的边,重复上一步。
-
对剩下每一个没有被配对的点执行步骤 1,直到所有的点都尝试完毕。
最小点覆盖
让每条边都至少和其中一个点关联。
二分图最小点覆盖集的大小=二分图最大匹配集的大小
最大独立集问题
最多点,互不相连
二分图最大独立集=图的点数-二分图最大匹配
DAG图的最小路径覆盖
解决此类问题可以建立一个二分图模型。把所有顶点 $i $拆成两个:X结点集中的 (i) 和Y结点集中的 (i') ,如果有边$i to j (,则在二分图中引入边)i to j'$,设二分图最大匹配为 (m) ,则结果就是$ n-m$
最小路径覆盖数=原有向图节点数-新二分图最大匹配数。
总结
通过以上定理,只要通过匈牙利或者网络流就可以求出最大匹配,然后直接计算答案。一般考点在于对题目的分析,学会如何建图,这样才能做出题。
KM算法
一些申明
- 顶标: 对于每个点有个值,要求 (forall i,j,A_{i}+B_{j} ge w(i,j)) ,(w(i,j)),为边权,无边时为 $-propto $ 。
- 交错树:每次dfs访问的点所形成的的一颗树
- 相等子图:如果对于子图所有节点 和 满足 (A_{i}+B_{j} = w(i,j)) 的边 ,那么这个子图为相等子图。
定理
若相等子图中存在完备匹配,则这个完备匹配为这个二分图的带权最大匹配
计算
为了保证顶标符合条件,在所有 (i in T,j otin T) 的边 ((i,j)) ,找出最小的(A_{i}+B_{j}-w(i,j)) 作为(Delta) ,在原图中寻找完备匹配即可。不断重复上述条件,直到每个左边点匹配成功
代码
// 二分图带权最大匹配:KM算法
const int N = 105;
int w[N][N]; // 边权
int la[N], lb[N]; // 左、右部点的顶标
bool va[N], vb[N]; // 访问标记:是否在交错树中
int match[N]; // 右部点匹配了哪一个左部点
int n, delta, upd[N];
bool dfs(int x) {
va[x] = 1; // 访问标记:x在交错树中
for (int y = 1; y <= n; y++)
if (!vb[y])
if (la[x] + lb[y] - w[x][y] == 0) { // 相等子图
vb[y] = 1; // 访问标记:y在交错树中
if (!match[y] || dfs(match[y])) {
match[y] = x;
return true;
}
}
else upd[y] = min(upd[y], la[x] + lb[y] - w[x][y]);
return false;
}
int KM() {
for (int i = 1; i <= n; i++) {
la[i] = -(1 << 30); // -inf
lb[i] = 0;
for (int j = 1; j <= n; j++)
la[i] = max(la[i], w[i][j]);
}
for (int i = 1; i <= n; i++)
while (true) { // 直到左部点找到匹配
memset(va, 0, sizeof(va));
memset(vb, 0, sizeof(vb));
delta = 1 << 30; // inf
for (int j = 1; j <= n; j++) upd[j] = 1 << 30;
if (dfs(i)) break;
for (int j = 1; j <= n; j++)
if (!vb[j]) delta = min(delta, upd[j]);
for (int j = 1; j <= n; j++) { // 修改顶标
if (va[j]) la[j] -= delta;
if (vb[j]) lb[j] += delta;
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans += w[match[i]][i];
return ans;
}
大致模型
- 一分为二一个点拆开两边各一个
- 行列模型
- 黑白染色模型
- 稳定婚姻模型
- 最小点覆盖
- 最大独立集
- 最小路径覆盖