acm.hdu.edu.cn/showproblem.php?pid=1198
较为隐蔽的并查集题目,但是分析发现还是统计集合个数的问题,需要注意的是集合的合并是有条件的,满足条件的合并,不满足的不合并。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 500;
int n,m,cnt,father[MAX*MAX],hash[MAX][MAX];
int d[12][4] = {{1,0,0,1},{1,1,0,0},{0,0,1,1},{0,1,1,0},{1,0,1,0},
{0,1,0,1},{1,1,0,1},{1,0,1,1},{0,1,1,1},{1,1,1,0},{1,1,1,1}};
//预处理每一块图案能连通的方向(上右下左)
int Move[][2] = {{-1,0},{0,1},{1,0},{0,-1}};//方向数组
char map[MAX][MAX];
bool II(int x, int y){
if(x < 0 || x >= n || y < 0 || y >= m)
return false;
return true;
}
void Init(){
for(int i=0; i<=n*m; i++)
father[i] = i;
}
int Find(int x){
if(x != father[x])
father[x] = Find(father[x]);
return father[x];
}
void Union(int x, int y){
int xx = Find(x), yy = Find(y);
father[yy] = xx;
return;
}
int main(){
while(scanf("%d%d",&n,&m) == 2){
if(n == -1 && m == -1) break;
Init();
for(int i=0; i<n; i++) cin >> map[i];
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
hash[i][j] = i*m+j;//哈希的思想给每个结点标号,标号必须不重复
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
for(int k=0; k<4; k++){
int dx = i + Move[k][0], dy = j + Move[k][1];
if(!II(dx, dy)) continue;
int idu = map[i][j] - 'A', idv = map[dx][dy] - 'A';
if(k == 0 && d[idu][0] && d[idv][2]) Union(hash[i][j], hash[dx][dy]);
else if(k == 1 && d[idu][1] && d[idv][3]) Union(hash[i][j], hash[dx][dy]);
else if(k == 2 && d[idu][2] && d[idv][0]) Union(hash[i][j], hash[dx][dy]);
else if(k == 3 && d[idu][3] && d[idv][1]) Union(hash[i][j], hash[dx][dy]);
}
}
}
cnt = 0;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
if(father[i*m+j] == i*m+j) cnt++;
printf("%d\n",cnt);
}
return 0;
}