当前位置:   article > 正文

NOIP2017 提高组 奶酪(DFS、BFS、并查集)一题三解_p3958 [noip2017 提高组] 奶酪

p3958 [noip2017 提高组] 奶酪

原文链接:NOIP真题第三讲:奶酪

题目来源:2017 年 NOIP 提高组 第一题

本题考察点:【DFS、BFS、并查集】

一、题目及链接

题目链接:

https://www.luogu.com.cn/problem/P3958

题意:老鼠是否可以从下表面的空洞一直沿着空洞走到上表面,如果可以,输出Yes,否则输出No;

二、问题分析

该题可以通过搜索来实现,找出所有的入口(即与下表面相切或相交的空洞)作为搜索的入口,在搜索的过程中,对已经搜过的空洞进行标记,每搜到一个空洞,判断是否为出口(即该空洞与上表面相交或相切),如果是,输出Yes,然后return,否则继续搜索,直到以所有入口为起点搜索完,还未找到出口,则说明不能从入口到出口,输出No并返回即可。

该问题还可以使用并查集来解决,暴力遍历任意两个空洞,检查,如果两个空洞相交或相切,则合并,遍历结束后,再分别遍历所有入口和出口,如果任意两个入口和出口是并查集中的同一合集,则说明可以从入口到出口,输出Yes,否则输出No;

三、问题解决

Q1: 如何判断一个空洞为入口或出口呢?

A1:如果为入口,则空洞的纵坐标z小于等于半径r;如果为出口,则空洞的纵坐标z加上半径r大于等于高度h;

Q2: 如何判断两个空洞是否相交或相切?

A2: 两个空洞的球心坐标距离小于等于半径的2倍即说明相交或相切。

Q3: 搜索过程中如何标记哪些点已经被搜索过呢?

A3: 用bool类型的vis数组标记。

下面我会分别使用DFS、BFS和并查集实现一次,请结合代码和注释一起阅读和思考。

四、AC Code

DFS

  1. #include "bits/stdc++.h"
  2. using namespace std;
  3. const int N = 1e3+7;
  4. long long t, n, h, r;
  5. bool vis[N];
  6. struct Node{ // 球心坐标
  7. int x, y ,z;
  8. }a[N];
  9. // 计算两点之间的距离,为避免小数,返回距离的平方(即省去开根号)
  10. long long dist(int idx1, int idx2) {
  11. return pow(a[idx1].x-a[idx2].x, 2) + pow(a[idx1].y-a[idx2].y, 2) +
  12. pow(a[idx1].z-a[idx2].z, 2);
  13. }
  14. // 检查两个空洞是否相切,相切或相交返回true,否则返回false
  15. bool check(int idx1, int idx2) {
  16. long long d = 4*r*r;
  17. if(dist(idx1, idx2) <= d) return true;
  18. return false;
  19. }
  20. // 判断是否为入口
  21. bool isIn(int idx) {
  22. return a[idx].z <= r;
  23. }
  24. // 判断是否为出口
  25. bool isOut(int idx) {
  26. return a[idx].z + r >= h;
  27. }
  28. // 从第idx个空洞是否可以搜索到出口,如果可以搜索到出口,返回true,否则返回false
  29. bool dfs(int idx) {
  30. if(vis[idx]) return false; // 已经被搜索过,返回false
  31. if(isOut(idx)) return true; // 搜索到了出口,返回true
  32. vis[idx] = true; // 标记
  33. for(int i=1; i<=n; i++) {
  34. if(vis[i]) continue; // 搜索过的不再搜索
  35. if(check(idx, i) && dfs(i)) return true; // 如果相交且能搜索出口,返回true
  36. }
  37. return false;
  38. }
  39. int main(){
  40. cin >> t;
  41. while(t--) {
  42. cin >> n >> h >> r;
  43. memset(vis, false, sizeof vis);
  44. for(int i=1; i<=n; i++) {
  45. cin >> a[i].x >> a[i].y >> a[i].z;
  46. }
  47. bool isTrue = false;
  48. for(int i=1; i<=n; i++) {
  49. // 如果没有被访问过且是入口且搜索到了出口,则输出YES,并结束本组数据,看下一组数据
  50. if(!vis[i] && isIn(i) && dfs(i)) {
  51. cout << "Yes" << endl;
  52. isTrue = true;
  53. break;
  54. }
  55. }
  56. if(!isTrue) cout << "No" << endl; // 如果没输出YES,则输出NO
  57. }
  58. return 0;
  59. }

BFS

  1. #include "bits/stdc++.h"
  2. using namespace std;
  3. const int N = 1e3+7;
  4. long long t, n, h, r;
  5. bool vis[N];
  6. queue<int> q;
  7. struct Node{ // 球心坐标
  8. int x, y ,z;
  9. }a[N];
  10. // 计算两点之间的距离,为避免小数,返回距离的平方(即省去开根号)
  11. long long dist(int idx1, int idx2) {
  12. return pow(a[idx1].x-a[idx2].x, 2) + pow(a[idx1].y-a[idx2].y, 2) +
  13. pow(a[idx1].z-a[idx2].z, 2);
  14. }
  15. // 检查两个空洞是否相切,相切或相交返回true,否则返回false
  16. bool check(int idx1, int idx2) {
  17. long long d = 4*r*r;
  18. if(dist(idx1, idx2) <= d) return true;
  19. return false;
  20. }
  21. // 判断是否为入口
  22. bool isIn(int idx) {
  23. return a[idx].z <= r;
  24. }
  25. // 判断是否为出口
  26. bool isOut(int idx) {
  27. return a[idx].z + r >= h;
  28. }
  29. void bfs() {
  30. while(!q.empty()) {
  31. int head = q.front(); // 拿出队头
  32. if(isOut(head)) { // 如果队头为出口
  33. cout << "Yes" << endl; // 输出并返回
  34. return;
  35. }
  36. q.pop(); // 弹出队头
  37. for(int i=1; i<=n; i++) {
  38. if(!vis[i] && check(i, head)) { // 没有访问并相交
  39. vis[i] = true;
  40. q.push(i); // 加入队列
  41. }
  42. }
  43. }
  44. cout << "No" << endl;
  45. return ;
  46. }
  47. int main(){
  48. cin >> t;
  49. while(t--) {
  50. cin >> n >> h >> r;
  51. memset(vis, false, sizeof vis);
  52. while(!q.empty()) q.pop();
  53. for(int i=1; i<=n; i++) {
  54. cin >> a[i].x >> a[i].y >> a[i].z;
  55. if(isIn(i)) { // 将所有的入口放入队列
  56. q.push(i);
  57. vis[i] = true; // 并标记
  58. }
  59. }
  60. bfs();
  61. }
  62. return 0;
  63. }

并查集

  1. #include "bits/stdc++.h"
  2. using namespace std;
  3. const int N = 1e3+7;
  4. long long t, n, h, r;
  5. unordered_set<int> st;
  6. struct Node{ // 球心坐标
  7. int x, y ,z;
  8. }a[N];
  9. int p[N]; // 并查集中的parent数组,p[i]表示i的父亲,初始时p[i]等于i,表示自己是自己的父亲
  10. // 计算两点之间的距离,为避免小数,返回距离的平方(即省去开根号)
  11. long long dist(int idx1, int idx2) {
  12. return pow(a[idx1].x-a[idx2].x, 2) + pow(a[idx1].y-a[idx2].y, 2) +
  13. pow(a[idx1].z-a[idx2].z, 2);
  14. }
  15. // 检查两个空洞是否相切,相切或相交返回true,否则返回false
  16. bool check(int idx1, int idx2) {
  17. long long d = 4*r*r;
  18. if(dist(idx1, idx2) <= d) return true;
  19. return false;
  20. }
  21. // 寻找child的根父亲节点,并查集的模板
  22. int findFather(int child) {
  23. if(child == p[child]) return p[child];
  24. p[child] = findFather(p[child]); // 并查集的压缩路径
  25. return p[child];
  26. }
  27. // 合并两个空洞为一个集合,并查集的模板
  28. void unionFind(int idx1, int idx2) {
  29. int p1 = findFather(idx1), p2 = findFather(idx2); // 找到各自的根父亲
  30. if(p1 != p2) p[p1] = p2; // 如果两个根父亲不是一个节点,则合并
  31. return;
  32. }
  33. // 判断是否为入口
  34. bool isIn(int idx) {
  35. return a[idx].z <= r;
  36. }
  37. // 判断是否为出口
  38. bool isOut(int idx) {
  39. return a[idx].z + r >= h;
  40. }
  41. // 初始化st为空,且p数组中p[i]=i;
  42. void init() {
  43. for(int i=1; i<=n; i++) p[i] = i;
  44. st.clear();
  45. }
  46. int main(){
  47. cin >> t;
  48. while(t--) {
  49. cin >> n >> h >> r;
  50. init(); // 更新p数组
  51. for(int i=1; i<=n; i++) {
  52. cin >> a[i].x >> a[i].y >> a[i].z;
  53. }
  54. for(int i=1; i<n; i++) {
  55. for(int j=i+1; j<=n; j++) {
  56. if(check(i, j)) unionFind(i, j); // 搜索任意两个空洞,如果相切或相交,合并
  57. }
  58. }
  59. for(int i=1; i<=n; i++) {
  60. if(isIn(i)) { // 如果第i个空洞为入口,则将其根父节点加入到st集合中
  61. int f = findFather(i); // 找到根父节点
  62. st.insert(f); // 插入st
  63. }
  64. }
  65. bool isTrue = false; // 是否找到相连的入口和出口
  66. for(int i=1; i<=n; i++) { // 遍历所有空洞
  67. // 如果是入口且该节点和出口有共同的根父节点,则一定可以到出口
  68. if(isOut(i) && st.count(findFather(i)) > 0) {
  69. cout << "Yes" << endl;
  70. isTrue = true;
  71. break;
  72. }
  73. }
  74. if(!isTrue) cout << "No" << endl; // 如果isTrue为false,说明不能从入口到出口,输出NO;
  75. }
  76. return 0;
  77. }

五、往期文章

NOIP真题第二讲:摆花

NOIP 真题第一讲:FBI 树

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/792382
推荐阅读
相关标签
  

闽ICP备14008679号