并查集 Farm Irrigation HDU - 1198

并查集

Farm Irrigation HDU - 1198

解题思路

事先存每个字母对应上下左右是否能接,是为 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

注意事项

  1. m a x n maxn maxn 一开始开了 500 500 500,一直TLE,找不出问题,后来改成 55 55 55 就好了orz。

解题总结

  1. m a x n maxn maxn 不要开太大。

  2. 巧用位运算。如判断 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;
}

猜你喜欢

转载自blog.csdn.net/Encore47/article/details/110325771