并查集
解题思路
事先存每个字母对应上下左右是否能接,是为 1 1 1,不是为 0 0 0。
之后遍历给出的字母地图,我只做了对右侧和对下侧的判断,若能接通则加入并查集,注意此时处理的是两个字母在数组中的编号。
计算方式为: n n n 行 m m m 列的数组 f a r m farm farm 中, f a r m farm farm[ i i i] [ j j j] 对应编号是 i × n + j i \times n + j i×n+j
注意事项
- m a x n maxn maxn 一开始开了 500 500 500,一直TLE,找不出问题,后来改成 55 55 55 就好了orz。
解题总结
-
m a x n maxn maxn 不要开太大。
-
巧用位运算。如判断 a a a 和 b b b 是否全为 1 1 1 时可以写:
if (a & b);
参考代码
//poj
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#include<deque>
#include<map>
#include<set>
#include<stack>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
//#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-6; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 55;
int readint(){
int x; scanf("%d", &x); return x;
}
struct point{
int h, r; //h代表relation
}s[maxn * maxn];
//并查集操作
void init_set() {
for(int i = 0; i < maxn * maxn; i++) {
s[i].r = i;
s[i].h = 1;
}
}
int find_set(int x) {
return x == s[x].r ? x : s[x].r = find_set(s[x].r);
}
void union_set(int x, int y) {
int rx = find_set(x);
int ry = find_set(y);
if (rx != ry) {
//这里优化
if (s[ry].h < s[rx].h) {
s[ry].r = rx;
s[rx].h += s[ry].h;
} else {
s[rx].r = ry;
s[ry].h += s[rx].h;
}
}
}
//从上开始,顺时针
int mp[11][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 dir[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}};
bool rjudge(char a, char b) { //a为左边,b为右边
return mp[a - 'A'][1] & mp[b - 'A'][3];
}
bool cjudge(char a, char b) { //下
return mp[a - 'A'][2] & mp[b - 'A'][0];
}
bool ujudge(char a, char b) {
return mp[a - 'A'][0] & mp[b - 'A'][2];
}
bool ljudge(char a, char b) {
return mp[a - 'A'][3] & mp[b - 'A'][1];
}
路径压缩的递推写法
防止爆栈
//int find_set(int x) {
// int r = x; //找根节点
// while (s[r] >= 0) r = s[r];
// int i = x, j; //路径压缩
// while (i != r) {
// j = s[i];
// s[i] = r;
// i = j;
// }
// return r;
//}
//
//void union_set(int r1, int r2) { //合并
// if (s[r1] > s[r2]) { //r1的元素少
// s[r2] += s[r1]; //一定先更新
// s[r1] = r2;
// } else {
// s[r1] += s[r2];
// s[r2] = r1;
// }
//}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int m, n;
char farm[maxn][maxn];
while (scanf("%d%d", &m, &n) && m >= 1 && n >= 1) {
init_set();
_for(i, 0, m) {
scanf("%s", farm[i]);
}
//按行遍历
_for(i, 0, m) {
_for(j, 0, n) {
int pos = i * n + j;
if (j <= n - 2) {
if (rjudge(farm[i][j], farm[i][j + 1])) {
union_set(pos, pos + 1);
}
}
if (i <= m - 2) {
if (cjudge(farm[i][j], farm[i + 1][j])) {
union_set(pos, pos + n);
}
}
}
// char now = farm[i][j];
// _for(k, 0, 4) {
// int dx = i + dir[k][0], dy = j + dir[k][1];
// int nextp = dx * n + dy;
// if (dx < 0 || dx >= m || dy < 0 || dy >= n) continue;
// char next = farm[dx][dy];
// if (k == 0) {
// if (ujudge(now, next)) union_set(pos, nextp);
// } else if (k == 1) {
// if (rjudge(now, next)) union_set(pos, nextp);
// } else if (k == 2) {
// if (cjudge(now, next)) union_set(pos, nextp);
// } else {
// if (ljudge(now, next)) union_set(pos, nextp);
// }
// }
// }
}
int ans = 0;
_for(i, 0, n * m) {
if (s[i].r == i) ans++;
}
printf("%d\n", ans);
}
return 0;
}