Acwing:泥地(二分图的匹配 —— 最小点覆盖)

二分图最小点覆盖应用:

证明可得:二分图的最大匹配数 == 最小点覆盖(证明理解不能,寄了)

什么是最小点覆盖?

最小点覆盖并不只在二分图中才存在

在任意一个图中选取 最少 多少个点,可以保证 这些点 覆盖 图中所有的边

这个数量就是最小点覆盖

————————————————————————————————————————————————

Acwing:泥地

此题为思维上更巧妙的最小点覆盖应用
在这里插入图片描述
此题木板覆盖泥地可以重叠

思路:

  • 记录每个点所在的泥地连续行、连续列(木板覆盖的是连续的区间),行对列连边 (这样的一条边上有两个点,可以理解为,要覆盖这个点(泥地)有 横着铺木板(行) 和 竖着铺木板(列) 两种方式)

  • 集合自然会分为行集合、列集合,满足二分图性质;最后求的就是,最少的点覆盖所有的边(所有的泥地点),即最小点覆盖

代码:

#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1) 
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2010, M = 500010, MM = 3000010;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int match[N], r[N][N], c[N][N], rcnt = 1, ccnt = 1;
//记录每个点所在的泥地连续行、连续列,行对列连边,集合自然分为行集合、列集合,满足二分图性质
//最后求的就是,最少的点覆盖所有的边,即最小点覆盖
bool st[N], g[N][N];
string s[60];
//注意一下数组大小

bool find(int x) { //标准匈牙利
	for (int j = 1; j <= ccnt; j++)
		if (!st[j] && g[x][j]) {
			st[j] = true;
			int t = match[j];

			if (!t || find(t)) {
				match[j] = x;
				return true;
			}
		}
	return false;
}

int main() {
	cinios;

	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> s[i];

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (j && s[i][j] == '*' && s[i][j - 1] == '.')rcnt++;
			//一旦遇到干净地面,泥地行编号++
			if (s[i][j] == '*')r[i][j] = rcnt;//赋予该格行编号
		}
		rcnt++;//换行也要++
	}

	//对列同样操作,小心bug
	for (int i = 0; i < m; i++) { //bug —— i < n, j < m
		for (int j = 0; j < n; j++) {
			if (j && s[j][i] == '*' && s[j - 1][i] == '.')ccnt++;
			//bug —— s[j][i - 1]
			if (s[j][i] == '*')c[j][i] = ccnt;//bug —— c[i][j]
		}
		ccnt++;
	}

	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			if (s[i][j] == '*') //对于泥地格,建边
				g[r[i][j]][c[i][j]] = true;

	int ans = 0;
	for (int i = 1; i <= rcnt; i++) { //行集合匹配列集合
		mem(st, 0);
		if (find(i))ans++;
	}
	cout << ans;

	return 0;
}

网络流(x)
抽象派(√)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值