五月集训(第26天)—并查集

并查集

1. 990. 等式方程的可满足性

    思路:
        如果 a==b则将a,b放入同一个集合(union)。最后判断,如果出现a!=b,而a,b在同一个集合里的情况,矛盾,返回false;否则,返回true。

class Solution {
    #define maxn 30
    int F[maxn];
public:
    // 初始化并查集
    void Build(int n) {
        for (int i = 0; i < n - 1; ++i) F[i] = i;  // 初始化
    }

    // 查找父节点 (路劲压缩)
    int find_f(int x) {
        if (F[x] != x) return F[x] = find_f(F[x]);
        return x;
    }

    // 集合合并
    void union_set(int a, int b) {
        int fa = find_f(F[a]);
        int fb = find_f(F[b]);
        if (fa == fb) return ;
        F[fa] = fb;
    }

    bool equationsPossible(vector<string>& equations) {
        int equations_size = equations.size();
        int i;
        Build(26);  /* 建立并查集 */
        for (i = 0; i < equations_size; ++i) {
            if (equations[i][1] == '=') union_set(equations[i][0] - 'a', equations[i][3] - 'a');
        }
        for (i = 0; i < equations_size; ++i) {
            if (equations[i][1] == '!') if (find_f(equations[i][0] - 'a') == find_f(equations[i][3] - 'a')) return false; 
        }
        return true;
    }
};

2. 1319. 连通网络的操作次数

    思路:
        如果connections.size() - 1 > 集合个数,随便拆一条边连接到集合与集合直接即可
        特判n - 1 > connections.size() ,边的个数少于点的个数 - 1,则一定不能实现完全连通。

class Solution {
    #define maxn  100010
    int F[maxn];
    bool F_flag[maxn];
public:
    void Build(int n) {
        for (int i = 0; i < n; ++i) F[i] = i;
        memset(F_flag, 0, sizeof(F_flag));
    }

    int find_f(int x) {
        if (F[x] == x) return x;
        return F[x] = find_f(F[x]);
    }

    void union_set(int a, int b) {
        int fa = find_f(a);
        int fb = find_f(b);
        if (fa != fb) F[fa] = fb;
        return ;
    }

    int makeConnected(int n, vector<vector<int>>& connections) {
        int connections_size = connections.size();
        int set_cnt = 0;
        Build(n);
		// 特判:边不够一定不能连接所有顶点
        if (n - 1 > connections_size) return -1;
		
		// 创建集合关系
        for (int i = 0; i < connections_size; ++i) {
            union_set(connections[i][0], connections[i][1]);
        }
		// 统计集合个数
        for (int i = 0; i < n; ++i) {
            int f_i = find_f(i);
            if (!F_flag[f_i]) {
                set_cnt++;
                F_flag[f_i] = true;
            }
        }

        return set_cnt - 1;
    }
};

3. 886. 可能的二分法

    思路:
        首先考虑将一个人a放入集合A,把他讨厌的人b放进集合B,后面再出现一个人c,将他放入集合A,将他讨厌的人d放入集合B,但是如果a讨厌c,那么不仅要把c从集合A放入B,还要把c讨厌的d放入集合A,当c有很多讨厌的人,就要逐个找出来放入A中,很麻烦。
        于是考虑对每个人创造一个对应的讨厌的人作为桥梁,将a与b讨厌的人b+n放入一个集合,将b与a讨厌的人a+n放入一个集合,如果再出现c,解可以利用a+n将c与b放入同一个集合,这样通过X+n这个媒介就可以直接实现将一个人与其讨厌的人交换集合的操作,很方便。

class Solution {
    #define N 2010
    int F[N << 1];  // 开两倍空间,0~N存每个人,N+1~2*N存每个人讨厌的人
public:
    void Build(int n) {
        int m = 2 * n;
        memset(F, 0, sizeof(F));
        for (int i = 0; i < m; ++i) F[i] = i;
    }

    void union_set(int a, int b) {
        int fa = find_f(a);
        int fb = find_f(b);
        if (fa != fb) F[fa] = fb;
    }

    int find_f(int x) {
        if (x == F[x]) return x;
        return F[x] = find_f(F[x]);
    }

    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
        int dislikes_size = dislikes.size();
        Build(n);
        
        for (int i = 0; i < dislikes_size; ++i) {
            int a = dislikes[i][0], b = dislikes[i][1];
            union_set(a, b + n);
            union_set(b, a + n);
            if (find_f(a) == find_f(b)) return false;
        }
        return true;
    }
};

posted @ 2022-06-02 14:51  番茄元  阅读(20)  评论(0)    收藏  举报