原题链接
解题思路
搜索
刚开始看到题目就想当然的认为是一道BFS,把整个矩阵看成0,1阵,连通用1表示,谁知道我当时怎么想到,很明显这样不能区分不同的块,唯一的价值就是找了编号和行列号的关系吧。
// 编号为a,则:
// 行号为(a-1)/n
// 列号(a-1)%n
本来想了想以为不同的块用不同的数字表示也可以做,比如第一块用1 ,第二块都用2,但是并不能保证读入顺序,还是无法分辨出不同的块。
并查集
用并查集的话那个矩阵就用不到了,就当成是 m ∗ n m*n m∗n个个体,用father数组值表示某元素属于哪个集合,初始化为自身,即每个元素所处的块编号都为自身编号,然后在读入连通编号时,将这两个元素所在的集合合并为一个集合。
源代码
没什么用的BFS
就当自己复习复习BFS了。
// 合根植物 BFS,不能全部设为1,无法区分不同的连通块
#include<iostream>
#include<queue>
using namespace std;
const int maxn = 1010;
int m, n;
struct Node{
int x, y; // 坐标
}temp;
bool matrix[maxn][maxn]; // 0,1矩阵表示连通性
bool inq[maxn][maxn]; // 各坐标位置的点是否已经入过队
// 增量矩阵
int X[4] = {0, 0, -1, 1};
int Y[4] = {-1, 1, 0, 0};
bool check(int x, int y){
// 判断该位置点是否有必要访问
if(x < 0 || x >= m || y < 0 || y >= n) // 越界
return false;
if(inq[x][y]) // 已入队
return false;
if(!matrix[x][y]) // 不连通
return false;
return true; // 除此之外的点都需要访问
}
void BFS(int x, int y){
// 将(x, y)所在连通块都设置为已入队即Inq = true
queue<Node> q;
Node node;
node.x = x;
node.y = y;
q.push(node);
inq[x][y] = true;
while(!q.empty()){
Node tmp = q.front();
q.pop();
for(int i=0; i<4; i++){
int newX = tmp.x + X[i];
int newY = tmp.y + Y[i];
if(check(newX, newY)){
temp.x = newX;
temp.y = newY;
q.push(temp);
inq[newX][newY] = true;
}
}
}
}
int main(){
fill(matrix[0], matrix[0]+maxn*maxn, false) ; // 0表示不连通
scanf("%d%d", &m, &n); // m行n列的矩阵
int k;
scanf("%d", &k);
int a, b;
for(int i=0; i<k; i++){
scanf("%d%d", &a, &b);
// 连通设为1
matrix[(a-1)/n][(a-1)%n] = 1;
matrix[(b-1)/n][(b-1)%n] = 1;
}
int res = 0; // 连通块的个数
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(matrix[i][j] &&!inq[i][j]){
res++;
BFS(i, j);
}
if(!matrix[i][j])
res++; // 单独一个植物,没有和别的合根
}
}
printf("%d\n", res);
return 0;
}
100分的并查集(有路径压缩,无按秩归并)
#include<iostream>
using namespace std;
const int maxn = 1000010;
int father[maxn]; // 表示各编号植物的父编号
int m, n; // m行n列
void init(int num){
for(int i=0; i<=num; i++){
father[i] = i; // 初始化父节点为自身
}
}
int find_father(int x){
// 返回x的父亲结点,并且进行路径压缩
int a = x;
while(x != father[x]){
x = father[x];
}
// x已经为父节点,从a开始再走一遍进行路径压缩
while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
void Union(int a, int b){
// 合并a,b所在的两个集合
int faA = find_father(a);
int faB = find_father(b);
if(faA != faB){
// TODO 这里应该实现按秩归并
father[faA] = faB;
}
}
int main(){
scanf("%d%d", &m, &n);
init(m*n);
int k;
scanf("%d", &k);
int a, b;
for(int i=0; i<k; i++){
scanf("%d%d", &a, &b);
Union(a, b);
}
int num = m*n;
int ans = 0;
for(int i=1; i<=m*n; i++){
if(father[i] == i){
ans ++ ;
}
}
printf("%d\n", ans);
return 0;
}