主页
Featured image of post ACM学习笔记——并查集……

ACM学习笔记——并查集……

数据结构——并查集……

文章字数:847
预计阅读时长: 分钟

该笔记还未完成整理,待日后继续整理……

该笔记目前直接复制的之前的笔记,还未调整格式。页面格式、内容、链接等可能存在混乱错误……

其实并不是笔记,
只是在例题中自我总结了一点相关知识_(:з」∠)_……

日后可能专门总结成笔记……

源码

/*
并查集算法
用father数组记录每个节点的父节点,数据结构类似于树
关键操作:合并、查询
详细介绍: https://zhuanlan.zhihu.com/p/93647900
*/
//P3958 奶酪: https://www.luogu.com.cn/problem/P3958
#include <bits/stdc++.h>
#define N 1005
using namespace std;
inline int quickRead()
{
    int f = 1, num = 0;
    char t = getchar();
    while (t < '0' || t > '9')
        f = t == '-' ? -1 : 1, t = getchar();
    while (t >= '0' && t <= '9')
        num = num * 10 + t - '0', t = getchar();
    return f * num;
}
struct typeUnionFind
{
    int num, father[N], rank[N]; //这里rank作用是记录节点深度(真实为深度-1),采用了按秩合并的优化
    /*
    按秩合并指将深度小的合并到深度大的树上,这样可以减少查询次数。
    这里按秩合并与路径压缩一起使用时,rank可能会因路径压缩操作而变得不准确。
    因此这里rank只是相对的判断标准,并不一定绝对会将深度小的合并到深度大的
    */

    void init(int n)
    {
        num = n;
        for (int i = 1; i <= num; i++)
            father[i] = i;
    }
    inline int find(int node)
    {
        return father[node] == node ? node : (father[node] = find(father[node])); //这里使用了路径压缩优化
    }
    inline void merge(int node1, int node2)
    {
        int father1 = find(node1), father2 = find(node2);
        if (rank[father1] < rank[father2])
            father[father1] = father2;
        else if (rank[father1] > rank[father2])
            father[father2] = father1;
        else if (father1 != father2)
            father[father1] = father2, rank[father2]++; //如果深度一样且父节点不一样(不为一棵树),合并后新树深度会加一
    }
} UnionFind;

struct typeHole
{
    long long x, y, z;
} hole[N];
bool ifIntersect(typeHole hole1, typeHole hole2, long long r)
{
    return (hole1.x - hole2.x) * (hole1.x - hole2.x) + (hole1.y - hole2.y) * (hole1.y - hole2.y) + (hole1.z - hole2.z) * (hole1.z - hole2.z) <= 4 * r * r;
}
int main()
{
    long long T, n, h, r;
    scanf("%lld", &T);
    while (T--)
    {
        scanf("%d%lld%lld", &n, &h, &r);
        UnionFind.init(n);
        UnionFind.father[1001] = 1001, UnionFind.father[1002] = 1002; //这里用特殊的两个节点,1001代表底,1002代表顶。如果father[1001]==father[1002]则证明连通。
        for (int i = 1; i <= UnionFind.num; i++)
        {
            scanf("%lld%lld%lld", &hole[i].x, &hole[i].y, &hole[i].z);
            if (hole[i].z <= r)
                UnionFind.merge(1001, i);
            if (hole[i].z >= h - r)
                UnionFind.merge(1002, i);
        }
        for (int i = 1; i <= UnionFind.num; i++)
            for (int j = 1; j <= UnionFind.num && i != j; j++)
                if (ifIntersect(hole[i], hole[j], r))
                    UnionFind.merge(i, j);
        if (UnionFind.find(1001) == UnionFind.find(1002))
            printf("Yes\n");
        else
            printf("No\n");
    }
}

/*
WA 1st:
没考虑到只有一个洞以及贯穿的情况……
修改:
1. merge(i,1001) ---> mergr(1001,i)
    实际上完全没必要,因为无论前并后还是后并前,最终都是父节点合并……
2. if (hole.z<=r) ... else if (hole.z>=h-r) ...; ---> if ...; if ...;
    当时想着要么是底部相交要么是顶部相交,导致用了个else if,没考虑到直接贯穿的情况……

WA 2nd:
long long!!!!……
*/