想过使用深度优先遍历,但是用优先遍历每次遍历都需要遍历每一个洞去查看是否存在与其相切或者相交的洞,付出的时间复杂度也挺大的。后来也可以想到使用并查集去求解,但遍历总是逃不掉的。又看到n的范围是1 ≤ n ≤ 1,000,所以两种方法都行。
并查集:将所有能都通过的洞组合到一个树里面,保持树的根是z值最大的那一个。然后记录下所有位于底部的点,最后遍历底部的点去寻找根,如果根可以到达顶部的话就证明没有问题。
要注意每轮过好要将原来数据该清空的清空。
#include <bits/stdc++.h>


using namespace std;
const int maxn = 1000+10;
struct Node {
    int par;
    int x, y, z;
} node[maxn];
int bottom[maxn];
int cnt = 0;

double dist(Node n1, Node n2) {
    return sqrt((double)pow(n1.x-n2.x, 2)+pow(n1.y-n2.y, 2)+pow(n1.z-n2.z, 2));
}

int find(int x) {
    if (node[x].par==x) return x;
    return node[x].par = find(node[x].par);
}

void Merge(int i, int j) {
    int i_root = find(i);
    int j_root = find(j);
    if (i_root==j_root) return ;
    if (node[i_root].z>node[j_root].z) {
        node[j_root].par = i_root;
    } else {
        node[i_root].par = j_root;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    
    while (T--) {
        cnt = 0;
        memset(bottom, 0, sizeof(bottom));
        int n, h, r;
        bool flag = false;
        cin>>n>>h>>r;
        for (int i=0;i<n;i++) {
            node[i].par = i;
        }
        for (int i=0;i<n;i++) {
            cin>>node[i].x;
            cin>>node[i].y;
            cin>>node[i].z;
            for (int j=0;j<i;j++) {
                if (dist(node[i], node[j])<=r*2) {
                    Merge(i, j);
                }
            }
            if (node[i].z-r<=0) {
                bottom[cnt++] = i;
            }
        }
        //根据所有的底部的空洞去寻找他们并查集的根部,如果根部的z是顶端z的话那么就证明可以到达顶部。
        for (int i=0;i<cnt;i++) {
            if (node[find(bottom[i])].z+r>=h) {
                flag = true;
                break;
            }
        }
        if (flag) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    
    
    return 0;
}
深度优先搜索的写法:
#include <bits/stdc++.h>


using namespace std;
const int maxn = 1000+10;
struct Node {
    int x, y, z;
} node[maxn];
int bottom[maxn];
int cnt = 0;
bool vis[maxn];
bool flag = false;
int n, h, r;

double dist(Node n1, Node n2) {
    return sqrt((double)pow(n1.x-n2.x,2)+pow(n1.y-n2.y,2)+pow(n1.z-n2.z,2));
}

void dfs(int pos) {
    vis[pos] = true;
    if (node[pos].z+r>=h) {
        flag = true;
        return ;
    }
    for (int i=0;i<n;i++) {
        if (vis[i]==false) {
//             vis[i] = true;
            if (dist(node[i], node[pos])<=r*2) {
                if (flag) {
                    break;
                }
                dfs(i);
            }
//             vis[i] = false;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while (T--) {
        cin>>n>>h>>r;
        flag = false;
//         memset(bottom, 0, sizeof(bottom));
        cnt = 0;
        for (int i=0;i<n;i++) {
            cin>>node[i].x;
            cin>>node[i].y;
            cin>>node[i].z;
            if (node[i].z-r<=0) {
                bottom[cnt++] = i;
            }
        }
        for (int j=0;j<cnt;j++) {
            memset(vis, 0, sizeof(vis));
            flag = false;
            dfs(bottom[j]);
            if (flag) break;
        }
        if (flag) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    
    return 0;
}
当然广度优先遍历也是可以的。