【学习笔记】二分图

感觉需要恶补一下二分图。

二分图的概念

对于一个无向图,如果其点集能被分为两部分,使得同一部内没有连边,那么这个图就是一个二分图

二分图有几个基本的性质:

  • 一个二分图可以被黑白染色,并且对其进行黑白染色后,同一部的颜色相同、
  • 从二分图的某一个点开始 BFS,那么所在的奇数层和偶数层分别为左部和右部的点。
  • 一个二分图的每一个环的长度都是偶数。

二分图的匹配

对于一个二分图,若它的边集的一个子集满足每两条边没有公共端点,那么称这个边集为一个匹配。

很多问题都可以转化为二分图的匹配问题,比如变量与值的匹配。

匈牙利算法

匈牙利算法的核心思想就是每一次从左部的一个点开始,寻找一个增广路,满足这条路径上匹配的边和未匹配的边交替出现,最后以未匹配的边结束。这条路的长度为奇数,然后将这条增广路上的边全部翻转,这样每一次就会使得匹配的边集扩大 \(1\),若无增广路则得到了最大匹配。

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 \(n\) 遍,于是复杂度为 \(O(nm)\)

网络流做法

考虑直接建网络流,源点连左部,右部连汇点,直接跑就行。

考虑每次 BFS+DFS 是 \(O(m)\) 的,而对于前 \(\sqrt m\) 轮增广复杂度为 \(O(m \sqrt m)\),而后 \(\sqrt m\) 次增广,增广路径长度大于 \(\sqrt m\),这样的路径不超过 \(\sqrt m\) 条,于是最多只会再进行 \(\sqrt m\) 轮增广,于是复杂度为 \(O(m \sqrt m)\)

最大权匹配

直接跑费用流吧

upd. 我错了,别跑费用流吧

KM 算法:求最大权完美匹配。如果两边点数不一样就补一下让两边一样。

考虑给每个点赋一个权值 \(l_i\),要求满足 \(w(u, v) \le l_u + l_v\),称为可行顶标。我们记左部点的可行顶标为 \(lx_i\),右部点的可行顶标为 \(ly_i\)

我们定义相等子图为,将所有满足 \(w(u, v) = lx_u + ly_v\) 的边保留下来得到的图。有定理:若存在某组可行顶标满足相等子图存在完美匹配,那么这组匹配就是最大全完美匹配。证明就放缩一下,可以得到匹配的权值一定小于等于 \(\sum lx_i + ly_i\),若存在完美匹配那么说明这组匹配一定取到了上界。

那么考虑类似于匈牙利的做法,每次考虑找到一个点在相等子图上的匹配,如果能增广成功则结束,否则考虑调整当前的可行顶标使得更多的边加入相等子图,进而存在增广路让匹配增加。考虑交错树(即匈牙利遍历到的所有点形成的树)上的点,记树上的左部点集合为 \(S\),右部点集合为 \(T\),不存在于交错树的集合为 \(S'\)\(T'\)。容易得到,不存在 \(S - T'\) 的边且不存在 \(S'-T\) 的匹配边(否则交错树可以扩大)。

考虑将 \(S\) 中的所有点减 \(d\)\(T\) 中的所有点加 \(d\),那么 \(S-T\) 的边仍然在相等子图内,\(S' - T'\) 内的边不会变,\(S - T'\) 中的 \(lx_u + ly_v\) 减少,可能加入相等子图,\(S' - T\) 中的 \(lx_u + ly_v\) 增加,使得全部从相等子图内移除,由于其本来就不存在匹配边所以不会影响最大匹配。那么我们令 \(d\)\(S - T'\) 的边中 \(lx_u + ly_v - w(u, v)\) 的最小值,这样会使得恰好这一条边加入相等子图。于是我们对 \(T'\) 中的点维护 \(slack(v) = \min \{lx_u + ly_v - w(u, v) | u \in S\}\),那么我们就可以 \(O(n)\) 计算出 \(d = \min\{slack(v) | v \in T'\}\)

每个点可能会进行 \(O(n)\) 次调整,每次调整后重新匹配是 \(O(n^2)\) 的,于是朴素实现是 \(O(n^4)\) 的。

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;
}

注意到问题在于每次重新匹配太浪费时间,所以可以复用上一次的匹配结果,从加入的边开始进行增广。额我真看不下去了先咕了,如果真碰到题卡我这个了再回来学吧。

最大匹配相关的转化

最小点覆盖 = 最大匹配

最大独立集 = \(n\) - 最小点覆盖

完美匹配

完美匹配即匹配的数量等于左部点的数量的匹配。

Hall 定理

一个二分图存在完美匹配的充要条件为:对于左部点的任意一个子集,其相连的右部的点集大小都大于等于这个子集的大小。

例子:

\(a_i \in [l_i, r_i]\),并且 \(a_i \not \in S\),求是否存在一组 \(a_i\) 的解,使得 \(a_i\) 互不相同。

将左侧 \(i\) 向右侧 \([l_i, r_i]\) 连边,就变成了是否存在完美匹配的问题。

考虑右部的点的集合,如果为几段不连续的区间,那么我们可以将它划分为几个不相交的子问题。

所以我们只考虑连续的区间,那么只需要满足左侧点的数量小于等于右侧点的数量,于是可以推出 \(\forall L\le R, \sum[L \le l_i \le r_i \le R] \le R - L + 1 - \sum_{i=L}^R[i \in S]\)

证明:如果 \([L, R]\) 是原来的一个区间,那么这个式子肯定没问题,如果不是原来的一个区间,那么必定会存在一个 \([L', R'] \subseteq [L, R]\)\(\sum[L \le l_i \le r_i \le R] = \sum[L' \le l_i \le r_i \le R']\),这时候已知 \([L', R']\) 对于上式是成立的,那么 \([L, R]\) 也一定成立。

二分图匹配的可行边和必须边

必须边的定义:二分图的任意一个最大匹配都包含的边。

可行边的定义:至少属于一个二分图的最大匹配的边。

首先对二分图跑一遍网络流。

必须边的判定条件:边的流量为 \(1\),并且两个端点在残量网络中不属于同一个 SCC。

可行边的判定条件:边的流量为 \(1\),或两个端点在残量网络中属于同一个 SCC。

这个东西与网络流中最小割的可行边和必须边是等价的。P4126

网格图模型

对网格图进行黑白染色,则图上相邻的两个点必定为一个黑点一个白点。P5038

P5030:这题比较有意思,可以发现能互相攻击到的点必定在同一种颜色的格子上,且横坐标的奇偶性一定不同,于是可以根据奇偶性来划分左部和右部,然后就是二分图最大匹配问题了。

posted @ 2022-11-14 15:40  APJifengc  阅读(109)  评论(4编辑  收藏  举报