百日训练(第四周 , 第一天)

本文介绍了如何利用并查集数据结构解决洛谷竞赛中的奶酪问题,包括联通洞的合并和判断是否能走出奶酪的策略,通过两次for循环实现并给出完整代码示例。
摘要由CSDN通过智能技术生成

放了一个五一假期,玩了五天,没有刷任何题,今天做一个恢复训练

洛谷 —— 奶酪

P3958 [NOIP2017 提高组] 奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

不难发现,这题可以用并查集,那么就有两个问题需要解决

1、如何将洞联通到一起

2、如何判断这个联通的洞是否可以出走奶酪

首先对于第一个问题,我们最开始想到的应该是两层for循环来将所有洞做一个合并,那么会超时吗,看到数据是1e3,那么应该不会超时,我们可以放心的去进行for循环

int find(int x){  //路径压缩
  return fa[x] = (fa[x] == x ? x : find(fa[x]));
}

void merge(int x , int y){ //并查集基本操作
  x = find(x) , y = find(y);
  if(x == y)return;
  fa[x] = y;
}

bool check(int d ,int b){  //判断两个点是否相交后或者是相切
  double x1 = a[d].x , x2 = a[b].x;
  double y1 = a[d].y , y2 = a[b].y;
  double z1 = a[d].z , z2 = a[b].z;
  double dis = sqrt((x1 -x2)*(x1 - x2) + (y1-y2)*(y1-y2) + (z1 - z2)*(z1-z2));

  return dis <=r+r;
}

  
for(int i =1;i<=n;++i){
    for(int j =i+1;j<=n;++j){
      if(check(i , j)){
        merge(i , j);
      }
    }
  }

第二个问题 , 如何判断一个联通的集合中,是否可以走出奶酪呢?

我们可以维护一个与顶部相交或相切的数组 , 和一个和底部相交或者相切的数组,再次进行两个for循环 , 循环上方与下方数组 , 看是否存在上方洞和下方洞在一个集合中 ,若有,就输出yes,否则输出no

下面直接放上完整代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second 
#define int long long
// #define ll long long
typedef pair<int,int> pii;
int gcd(int x , int y){
  return y ==0?x : gcd(y , x%y);
}
int lcm(int x , int y){
  return x*y/gcd(x,y);
}
// const int mod = 998244353;
const int N  = 1e5+9;
struct node
{
  double x , y ,z;
}a[N];
int n , h , r;
int fa[N] ,tot1[N] , tot2[N];

int find(int x){
  return fa[x] = (fa[x] == x ? x : find(fa[x]));
}

void merge(int x , int y){
  x = find(x) , y = find(y);
  if(x == y)return;
  fa[x] = y;
}

bool check(int d ,int b){
  double x1 = a[d].x , x2 = a[b].x;
  double y1 = a[d].y , y2 = a[b].y;
  double z1 = a[d].z , z2 = a[b].z;
  double dis = sqrt((x1 -x2)*(x1 - x2) + (y1-y2)*(y1-y2) + (z1 - z2)*(z1-z2));

  return dis <=r+r;
}


void solve(){
  memset(fa , 0 , sizeof(fa));
  memset(a , 0 ,sizeof(a));
  cin>>n>>h>>r;
  for(int i =1;i<=n;++i)cin>>a[i].x>>a[i].y>>a[i].z;
  for(int i =1;i<=n;++i)fa[i] = i;
  int cnt1 = 1 , cnt2 = 1;
  for(int i =1;i<=n;++i){
    if(a[i].z + r >= h){
      tot1[cnt1] = i;
      cnt1++;
    }
    if(a[i].z - r <=0){
      tot2[cnt2] = i;
      cnt2++;
    }
  }
  cnt1--;
  cnt2--;


  for(int i =1;i<=n;++i){
    for(int j =i+1;j<=n;++j){
      if(check(i , j)){
        merge(i , j);
      }
    }
  }

  for(int i =1;i<=cnt1;++i){
    for(int j =1;j<=cnt2;++j){
      if(find(tot1[i]) == find(tot2[j])){
        cout<<"Yes"<<endl;
        return;
      }
    }
  }
  cout<<"No"<<endl;

}

signed main(){
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int _ =1;
    
    cin>>_;
    
    while(_--){
      solve();
    }
    
    return 0;
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值