【蓝桥杯每日一题】4.1 奶酪

我天!哥们经过两个周的忙碌又重生了!

原题链接:528. 奶酪 - AcWing题库

本题注意点:

  • 注意几个边界值,如果说没有球连接顶部或者底部,老鼠是不可能上来的,直接say no!
  • 要利用公式判断两个球是相切or相交 ,相离是没有可能的,也是直接say no!
  • 主要距离公式:
    d i s t = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 + ( z 1 − z 2 ) 2 {dist}= \sqrt{ (x_1 - x_2)^2 + (y_1 - y_2)^2 + (z_1 - z_2)^2 } dist=(x1x2)2+(y1y2)2+(z1z2)2

看题目的大意,有点像查找最小连通图的影子,现在先用并查集解决问题:

解法1:并查集

时间复杂度: T ( n 2 ) T(n^2) T(n2),运行时间753ms

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;

int t;
int p[N];
int n,h,r;

struct Spot{
	ll x,y,z;
}a[N];


int find(int x){
    if(p[x]!=x) {p[x]=find(p[x]);}
    return p[x];   
}

int judge(ll x1,ll x2,ll y1,ll y2,ll z1,ll z2,ll r)//判断两个空洞是否有交集
{
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)<=(4*r*r)?1:0;
}

void merge(int x,int y){
    p[find(x)]=find(y);
}
int main()
{
	scanf("%d",&t);
    while(t--)
	{
        scanf("%d%d%d",&n,&h,&r);
        
           
        for(int i=1;i<=n;i++)
        {
        	a[i].x=a[i].y=a[i].z=0;    //一定要清空数组
        	p[i]=i;  //并查集初始化
		}
		
		//0代表奶酪底部,1001代表奶酪顶部
		p[0]=0,p[1001]=1001;
        for(int i=1;i<=n;i++)
		{
            cin>>a[i].x>>a[i].y>>a[i].z;
            if(a[i].z-r<=0){   //空洞最低点在奶酪外或与奶酪相切,与底部合并
                merge(i,0);
            }
            if(a[i].z+r>=h){//同理,最高点在奶酪外或相切,与顶部合并
                merge(i,1001);
            }
            
        }
    	
        for(int i=1;i<=n;i++)
		{
            for(int j=i+1;j<=n;j++)
			{
                if(judge(a[i].x,a[j].x,a[i].y,a[j].y,a[i].z,a[j].z,r)){//有交集就合并
                    merge(i,j);
                }
            }
        }
        if(find(0)==find(1001)) printf("Yes\n");//底部和顶部相连通即根节点相同
        else printf("No\n");
    }
    return 0;
}

解法2:dfs(深度优先搜索)

主要思想:用空间换取时间

AC 代码:

//利用dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;


int t;
int vis[N];
int found; //用于插眼
int n,h,r;

struct Spot{
	ll x,y,z;
}a[N];

int judge(ll x1,ll x2,ll y1,ll y2,ll z1,ll z2,ll r){
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)<=(4*r*r)?1:0;
}
void dfs(int m){
    if(a[m].z+r>=h)
	{//递归出口,即搜到顶端
        found=1;
        return;
    }
    vis[m]=1;//标记走过
    for(int i=1;i<=n;i++)
	{
        if(!vis[i]&&judge(a[i].x,a[m].x,a[i].y,a[m].y,a[i].z,a[m].z,r))
		{//没有访问且两个空洞有交集,继续搜索
            dfs(i);
        }
    }
}
int main(){
    
    cin>>t;
    while(t--){
        found=0;    //初始化
    
        for(int i=1;i<=n;i++)
        {
        	a[i].x=a[i].y=a[i].z=0;    //一定要清空数组
		}
        memset(vis,0,sizeof(vis));
        
        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++)
		{
            if(!vis[i]&&a[i].z<=r)
			{//没有访问过并且和底部相连通,即dfs入口
                dfs(i);
            }
            if(found==1)break;
        }
        
        
        if(found==1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值