二分图总结

二分图总结

匈牙利算法

基于贪心的思想。

  • 首先从任意一个未配对的点 \(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 \notin 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;
}

大致模型

  • 一分为二一个点拆开两边各一个
  • 行列模型
  • 黑白染色模型
  • 稳定婚姻模型
  • 最小点覆盖
  • 最大独立集
  • 最小路径覆盖
posted @ 2020-04-12 20:38  ztz_cpp  阅读(114)  评论(0编辑  收藏  举报