Codeforces -1012B Chemical table (思维+并查集)

题目链接

题意:给出一个N*M的空矩阵,然后给出一个Q,随后Q行是Q个点的x和y坐标,若已知这样三个点(x1,y1),(x2,y1),(x1,y2),可以在(x2,y2)处生成一个新的点,对于新的点和被使用过的点都能重复使用,问你最少需要在矩阵汇总添加多少个点是的整个矩阵被点铺满?

题解:通过画图可以发现我们对于一个N*M的空矩阵,我们最少填加n+m-1个点就能再通过题意给出的操作不断生成新的点铺满整个图.而且对于题目给出的操作,我们可以发现有这样的规律:

        当插入点(x1,y1) 时有关系x1<=>y1

        当插入点(x2,y1) 时有关系 x2<=>y1<=>x1

        当插入点(x1,y2) 时有关系 y2<=>x1<=>y1<=>x2

        我们发现这时候点(x2,y2)很自然的就获得了~

很显然上面的思想可以通过并查集来实现,不过具体值需要建立单向边即可,且需要预先对Y坐标进行处理编号为N+1~N+M,剩下的就是判断最少添加多少个关系可以使得所有N+M个坐标关系处于同一集合,具体看代码~

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 4e5 + 10;
int f[maxn];
int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}
void unite(int x, int y) {
	int xx = find(x);
	int yy = find(y);
	if (xx != yy)
		f[xx] = yy;
}
void init(int n) {
	for (int i = 1; i <= n; i++)
		f[i] = i;
}
int main() {
	int n, m, q, x, y;
	cin >> n >> m >> q;
	init(n + m);
	for (int i = 0; i < q; i++) {
		cin >> x >> y;
		unite(x, y + n);          //注意这里是y+n,目的:将纵坐标1~m编号为n+1~n+m
	}
	int ans = 0;
	int root = find(1);           //选一个点的根作为root节点
	for (int i = 2; i <= n + m; i++) {
		if (find(i) != root) {    //若是该链是孤立的,将其根直接连到根上(表示为图中增加一个点从而获得连通关系)
			ans++;
			f[find(i)] = root;    
		}
	}
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/81315400