题意:给出一个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;
}