前言

博主很笨 ,如有纰漏,欢迎在评论区指出讨论。

二分图的最大匹配使用 \(Dinic\) 算法进行实现,时间复杂度为 \(O(n\sqrt{e})\),其中, \(n\)为二分图中左部点的数量, \(e\) 为二分图中的边数。若是匈牙利算法,时间复杂度为 \(O(nm)\) , \(m\) 为二分图中右部点的数量,不建议使用。

文章中的例题链接。

König定理

定理内容:二分图最小点覆盖的点的数量等于二分图最大匹配的边的数量。

构造方法 \(+\) 简单证明:

首先求出二分图中的最大匹配,建议使用 \(Dinic\) 。

从每一个非匹配点出发,沿着非匹配边正向进行遍历,沿着匹配边反向进行遍历到的点进行标记。选取左部点中没有被标记过的点,右部点中被标记过的点,则这些点可以形成该二分图的最小点覆盖。

遍历代码实现如下:

void dfs(int now) {
vis[now] = true;
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i].to;
if(vis[next] || !v[now][i].val)//正向边的容量为0说明是匹配边,反向边的容量为0说明是非匹配边
continue;
dfs(next);
}
}

那么就有以下性质:

  • 若该点为左边的非匹配点,则这个点必被访问,因为这个点是整个 \(dfs\) 的起点
  • 若该点为右边的非匹配点,则这个点必不会被访问,若是由左边的非匹配点才到达了这个点,那么可以将这条边变为匹配边,则匹配数 \(+1\) ,与最大匹配相冲突。若是左边的匹配点才到达了这个点,那么这个点的路径为左边非匹配点右边匹配点左边非匹配点右边匹配点……左边匹配点右边非匹配点 ,很明显,上述路径为增广路,与最大匹配相冲突。所以,右边的非匹配点必不会被访问。
  • 对于一组匹配点,要么两个都被标记,要么都不被标记。因为左部的匹配点是由右部的匹配点来遍历到的,出现必然成双成对。

有了上述的三条性质,可以发现:按照选取左部点中没有被标记过的点,右部点中被标记过的点的规则,选出来的点的点数必然为最大匹配的边数。左部的非匹配点必然被访问,则必不会被选,右部的非匹配点必不会被访问,则必不会被选。而第三条性质决定了,对于一组匹配点,会选择有且仅有一个点。故而选出的点的点数等于最大匹配的边数。

其次需要解决一个问题:保证这些点覆盖了所有的边。具体可以分为四类:

  • 左部为非匹配点,右部为非匹配点。性质二已经讨论过,不可能出现这种情况,出现就不满足最大匹配的前提。
  • 左部为匹配点,右部为非匹配点。同理性质二,路径类似,会出现增广路,那么这个左部的匹配点一定没有被访问过,必然被选。
  • 左部为匹配点,右部为匹配点。一对匹配点中必选一个。
  • 左部为非匹配点,右部为匹配点。这条边为非匹配边,而起点就是从左部的非匹配点点开始,那么右部的这个点必然被访问过,必然被选。

最后在确保这是最小的方案:一条边都只选了一个点,不存在浪费。

如上,证毕。

题目来源:COCI 2019/2020 Contest #6 T4. Skandi

题目大意

给定一个 \(n\times m\) 的矩阵,其中的白色点为 \(0\) , 黑色点为 \(1\) 。黑色点可以往下一直扩展到底部,把白色点变成蓝色点,直到遇到黑色点为止。同理,也可向右扩展。问整个矩阵经过最小多少次扩展才能扩展为整个矩阵到不存在白色,并打印出每次扩展是从哪个点开始的,并打印出扩展方向。题目满足第一行第一列一定为黑色点。

思路

一道建模题。

一个白色点变为蓝色点只有两种方法,从它上方或左方的黑色点扩展而来,且只需要一个点扩展即可。可以考虑到最小点覆盖问题。

由于对于一个黑色点来说,它可以往右或往下扩展。那么它就有两个身份,也就是说一个点拥有两个编号。一个编号为把整个矩阵拉成一条链的顺序,另一个编号为前一个编号 \(+n\times m\) ,这样不会发生冲突。获得编号的函数:

int GetHash(int i, int j) {
return (i - 1) * m + j;
}

那么不难发现一个白色点,与其相关的是一个编号 \(\leqslant n\times m\) 的点,和一个编号 \(>n\times m\) 的点。把这两个点连接起来,就是一张二分图。

问题就转换为找这张图的最小点覆盖问题。使用 \(Dinic\) ,在根据上述 \(König\) 定理构造即可。

边数为白点的个数,左部点为黑点的个数,则时间复杂度为 \(O(nm\sqrt{nm})\) ,即 \(O(n^{\frac{3}{2}}m^{\frac{3}{2}})\) ,本题的 \(n\) , \(m\) 均小于 \(500\) ,大概能够在 \(1s\) 内求出答案。

C++代码

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN = 1e6 + 5;
const int MAXM = 5e2 + 5;
struct Node {
int to, val, rev;//依次为:下一个点,边的容量,相反的边的编号
Node() {}
Node(int T, int V, int R) {
to = T;
val = V;
rev = R;
}
};
vector<Node> v[MAXN];//用vector存图的癖好...
int dn[MAXN], rt[MAXN];//预处理白色点可以右那两个点扩展而来
queue<int> q;
int de[MAXN], be[MAXN];
int twin[MAXN];
bool vis[MAXN];
int n, m, s, t;
int arr[MAXM][MAXM];
bool bfs() {//将残量网络分层
bool flag = 0;
memset(de, 0, sizeof(de));
while(!q.empty())
q.pop();
q.push(s);
de[s] = 1; be[s] = 0;
while(!q.empty()) {
int now = q.front();
q.pop();
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i].to;
if(v[now][i].val && !de[next]) {
q.push(next);
be[next] = 0;
de[next] = de[now] + 1;
if(next == t)
flag = 1;
}
}
}
return flag;
}
int dfs(int now, int flow) {//沿着增广路增广
if(now == t || !flow)
return flow;
int i, surp = flow;
int SIZ = v[now].size();
for(i = be[now]; i < SIZ && surp; i++) {
be[now] = i;
int next = v[now][i].to;
if(v[now][i].val && de[next] == de[now] + 1) {
int maxnow = dfs(next, min(surp, v[now][i].val));
if(!maxnow)
de[next] = 0;
v[now][i].val -= maxnow;
v[next][v[now][i].rev].val += maxnow;
surp -= maxnow;
}
}
return flow - surp;
}
int Dinic() {//网络最大流,亦可用于二分图匹配
int res = 0;
int flow = 0;
while(bfs())
while(flow = dfs(s, INF))
res += flow;
return res;
}
int GetHash(int i, int j) {//获取点的编号
return (i - 1) * m + j;
}
void Down(int now, int i, int j) {//黑点向下扩展,每个白点最多遍历到一次
if(i != now)
dn[GetHash(now, j)] = GetHash(i, j);
if(arr[now + 1][j] == 2)
Down(now + 1, i, j);
}
void Right(int now, int i, int j) { //黑点向右扩展,每个白点最多遍历到一次
if(j != now)
rt[GetHash(i, now)] = GetHash(i, j) + n * m;
if(arr[i][now + 1] == 2)
Right(now + 1, i, j);
}
void GetMin(int now) {//dfs求构造方式
vis[now] = true;
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i].to;
if(vis[next] || !v[now][i].val)
continue;
GetMin(next);
}
}
int main() {
scanf("%d %d", &n, &m);
s = 0; t = 2 * n * m + 1;//源点和汇点初始化
char ch;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> ch;
if(ch == '1')
arr[i][j] = 1;
else
arr[i][j] = 2;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(i == 1 && j == 1)
continue;
if(arr[i][j] == 1) {//向右或向下扩展,一个白点会被访问2次
Down(i, i, j);
Right(j, i, j);
}
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) {
if(arr[i][j] == 1) {//源点到左部点,汇点到右部点连边
int now = GetHash(i, j);
int idnow = v[now].size();
int ids = v[s].size();
v[s].push_back(Node(now, 1, idnow));
v[now].push_back(Node(s, 0, ids));
now = GetHash(i, j) + n * m;
idnow = v[now].size();
int idt = v[t].size();
v[now].push_back(Node(t, 1, idt));
v[t].push_back(Node(now, 0, idnow));
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(i == 1 && j == 1)
continue;
if(arr[i][j] == 1)
continue;
int A = dn[GetHash(i, j)];//左部点到右部点连边
int B = rt[GetHash(i, j)];
int idA = v[A].size();
int idB = v[B].size();
v[A].push_back(Node(B, 1, idB));
v[B].push_back(Node(A, 0, idA));
}
}
printf("%d\n", Dinic());
GetMin(s);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(arr[i][j] == 2)
continue;
if(!vis[GetHash(i, j)])//打印答案
printf("%d %d DOLJE\n", i, j);
if(vis[GetHash(i, j) + n * m])
printf("%d %d DESNO\n", i, j);
}
}
return 0;
}

二分图最小点覆盖构造方案+König定理证明的更多相关文章

  1. UVA1194 Machine Schedule[二分图最小点覆盖]

    题意翻译 有两台机器 A,B 分别有 n,m 种模式. 现在有 k 个任务.对于每个任务 i ,给定两个整数$ a_i\(和\) b_i$,表示如果该任务在 A上执行,需要设置模式为 \(a_i\): ...

  2. POJ2226 Muddy Fields(二分图最小点覆盖集)

    题目给张R×C的地图,地图上*表示泥地..表示草地,问最少要几块宽1长任意木板才能盖住所有泥地,木板可以重合但不能盖住草地. 把所有行和列连续的泥地(可以放一块木板铺满的)看作点且行和列连续泥地分别作 ...

  3. POJ1325 Machine Schedule(二分图最小点覆盖集)

    最小点覆盖集就是在一个有向图中选出最少的点集,使其覆盖所有的边. 二分图最小点覆盖集=二分图最大匹配(二分图最大边独立集) 这题A机器的n种模式作为X部的点,B机器的m种模式作为Y部的点: 每个任务就 ...

  4. hihoCoder #1127:二分图最小点覆盖和最大独立集

    题目大意:求二分图最小点覆盖和最大独立集. 题目分析:如果选中一个点,那么与这个点相连的所有边都被覆盖,使所有边都被覆盖的最小点集称为最小点覆盖,它等于最大匹配:任意两个点之间都没有边相连的最大点集称 ...

  5. [POJ] 2226 Muddy Fields(二分图最小点覆盖)

    题目地址:http://poj.org/problem?id=2226 二分图的题目关键在于建图.因为“*”的地方只有两种木板覆盖方式:水平或竖直,所以运用这种方式进行二分.首先按行排列,算出每个&q ...

  6. 二分图 最小点覆盖 poj 3041

    题目链接:Asteroids - POJ 3041 - Virtual Judge  https://vjudge.net/problem/POJ-3041 第一行输入一个n和一个m表示在n*n的网格 ...

  7. HihoCoder1127 二分图三·二分图最小点覆盖和最大独立集

    二分图三·二分图最小点覆盖和最大独立集 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在上次安排完相亲之后又过了挺长时间,大家好像都差不多见过面了.不过相亲这个事不是说 ...

  8. hdu 2236(二分图最小点覆盖+二分)

    无题II Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  9. 四川第七届 D Vertex Cover(二分图最小点覆盖,二分匹配模板)

    Vertex Cover frog has a graph with nn vertices v(1),v(2),…,v(n)v(1),v(2),…,v(n) and mm edges (v(a1), ...

随机推荐

  1. docker(5)docker运行web应用

    前言 前面我们运行的容器并没有一些什么特别的用处. 接下来让我们尝试使用 docker 构建一个 web 应用程序. 我们将在docker容器中运行一个 Python Flask 应用来运行一个web ...

  2. Educational Codeforces Round 39

    Educational Codeforces Round 39  D. Timetable 令\(dp[i][j]\)表示前\(i\)天逃课了\(j\)节课的情况下,在学校的最少时间 转移就是枚举第\ ...

  3. 2018 ccpc吉林 The Tower

    传送门:HDU - 6559 题意 在一个三维空间,给定一个点和他的三维速度,给定一个圆锥,问这个点最早什么时候能撞上圆锥. 题解 本来一直想着怎么求圆锥的方程,然后....队友:这不是二分吗!然后问 ...

  4. C - Last Digit

    Description The function f(n, k) is defined by f(n, k) = 1k + 2k + 3k +...+ nk. If you know the valu ...

  5. hdu5233 Gunner II

    Problem Description Long long ago, there was a gunner whose name is Jack. He likes to go hunting ver ...

  6. Codeforces Round #540 (Div. 3) C. Palindromic Matrix (大模拟)

    题意:给你\(n\)个数,判断是否能构成一个\(n\)X\(n\)的回文矩阵,若可以,输出\(YES\)和矩阵,否则输出\(NO\). 题解:如果这个矩阵的行/列元素是偶数的话,很好办,所有出现的数一 ...

  7. Cobbler自定义标题及菜单密码

    sha1pass mypassword || openssl passwd -1 -salt sXiKzkus mypassword $1$sXiKzkus$x12Z3ZaiC34GhceLH5LXw ...

  8. 导出Excel的异常处理

    问题: 提示:"类 Range 的 Select 方法无效" 处理方法: 设置当前工作表 this.worksheet.Activate();

  9. poj-3046 Ant Counting【dp】【母函数】

    题目链接:戳这里 题意:有A只蚂蚁,来自T个家族,每个家族有ti只蚂蚁.任取n只蚂蚁(S <= n <= B),求能组成几种集合? 这道题可以用dp或母函数求. 多重集组合数也是由多重背包 ...

  10. 左神算法第五节课:认识哈希函数和哈希表,设计RandomPool结构,布隆过滤器,一致性哈希,岛问题,并查集结构

    认识哈希函数和哈希表 MD5Hash值的返回范围:0~9+a~f,是16位,故范围是0~16^16(2^64)-1, [Hash函数],又叫散列函数: Hash的性质: 1)  输入域无穷大: 2)  ...