当前位置: 首页 > news >正文

[Luogu 3958] NOIP2017 D2T1 奶酪

题目链接


人生第一篇题解,多多关照吧。


注意事项:

1.多组数据,每次要先初始化
2.因为涉及到开根,所以记得开double

整体思路:

建图,判断「起点」与「终点」是否连通。
方法可选择搜索(我写的BFS)或并查集(UFS)。
首先,读入时记录这些球的最小高度和最大高度,如果最低的球与底面相离,或是最高的球与顶面相离,直接Pass。
我们会发现,可能不止一个球与底面相切或相交,也可能不止一个球与顶面相切或相交。
这就是说,起点和终点都可能不止一个,这给我们操作造成了一些麻烦(然而考场上我就这么硬搜的居然AC了)。
其实,通过建立「超级起点」「超级终点」,可以把操作变得简单——用结构体数组的第0个元素表示「超级起点」,第n+1个元素表示「超级终点」。

两种方法都需要进行的预处理操作:

对于每一组球(i,j),计算两球球心距离是否小于半径×2。
走一遍所有的球,如果当前球可以做起点,就连上当前球和超级起点;终点亦然。

方法1——BFS:

用二维bool数组e[i][j]记录i能否到达j,相当于存图(链式前向星也可以的,只是本题数据范围没有必要)。
对于每一个球,依次判断其能否到达0..n+1,当「超级终点」已被访问或队列已为空时结束搜索。
如果「超级终点」被访问过说明搜到了,可以到达;否则无法到达。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN=1010;
bool vis[MAXN],e[MAXN][MAXN];
double h,r,low,high;
int T,n;
struct ball
{
    double x,y,z;
}s[MAXN];
double dis(ball a,ball b)
{
    double t1=a.x-b.x,t2=a.y-b.y,t3=a.z-b.z;
    return sqrt(t1*t1+t2*t2+t3*t3);
}
void Init()//一定记得初始化
{
    low=h,high=0;
    memset(vis,0,sizeof vis);
    memset(e,0,sizeof e);
}
void Pre()//预处理
{
    for(int i=1;i<=n;++i)
    {
        if(s[i].z-r<=0)//超级起点
            e[0][i]=e[i][0]=1;
        if(s[i].z+r>=h)//超级终点
            e[n+1][i]=e[i][n+1]=1;
    }
    for(int i=1;i<n;++i)
        for(int j=i+1;j<=n;++j)
            e[i][j]=e[j][i]=dis(s[i],s[j])<=r*2;//两球是否相连
}
void BFS()
{
    queue<int> q;
    q.push(0);
    vis[0]=1;
    while(!vis[n+1] && !q.empty())//超级终点被搜到了,或队列已空
    {
        int x=q.front();
        q.pop();
        for(int i=0;i<=n+1;++i)
            if(!vis[i] && e[x][i])
            {
                q.push(i);
                vis[i]=1;
            }
    }
}
int main(int argc,char *argv[])
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %lf %lf",&n,&h,&r);
        Init();//初始化
        for(int i=1;i<=n;++i)
        {
            scanf("%lf %lf %lf",&s[i].x,&s[i].y,&s[i].z);
            low=min(low,s[i].z),high=max(high,s[i].z);//记录最小和最大高度
        }
        if(low-r>0 || high+r<h)//需要Pass的情况
        {
            printf("No\n");
            continue;
        }
        Pre();//预处理
        BFS();
        printf(vis[n+1]?"Yes\n":"No\n");//超级终点是否被访问
    }
    return 0;
}

方法2——UFS:

预处理时,如果两个球可以相连,就合并这两个球所在的UFS。
最终判断0和n+1两个球是否属于同一UFS,是则Yes,否则No。

并查集写法的代码更新于 2018.05.27,在一次水模拟赛中,原题写炸,遂重写,以前的代码风格是什么玩意!

#include <algorithm>
#include <cmath>
#include <cstdio>
using std::max;
using std::min;
const int MAXN=1010;
double h,r;
int T,n;
struct Ball
{
    double x,y,z;
    void Read(void)
    {
        scanf("%lf %lf %lf",&x,&y,&z);
    }
    friend double Dist(const Ball &a,const Ball &b)
    {
        double x=a.x-b.x,y=a.y-b.y,z=a.z-b.z;
        return sqrt(x*x+y*y+z*z);
    }
    bool operator <(const Ball &rhs) const
    {
        return z<rhs.z;
    }
}s[MAXN];
class UFS
{
    private:
        int f[MAXN];
        int Find(int x)
        {
            return x==f[x] ? x : f[x]=Find(f[x]);
        }
        void Merge(int x,int y)
        {
            f[Find(y)]=f[Find(x)];
        }
    public:
        UFS(int n)
        {
            for(int i=0;i<=n+1;++i)
                f[i]=i;
            for(int i=1;i<=n;++i)
            {
                if(s[i].z-r<=0)
                    Merge(0,i);
                if(s[i].z+r>=h)
                    Merge(i,n+1);
            }
            for(int i=1;i<n;++i)
                for(int j=i+1;j<=n;++j)
                    if(Dist(s[i],s[j])<=2*r)
                        Merge(i,j);
        }
        bool Connected(int x,int y)
        {
            return Find(x)==Find(y);
        }
};
int main(int argc,char** argv)
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %lf %lf",&n,&h,&r);
        double low=h,high=0;
        for(int i=1;i<=n;++i)
        {
            s[i].Read();
            low=min(low,s[i].z);
            high=max(high,s[i].z);
        }
        if(low-r>0 || high+r<h)
        {
            puts("No");
            continue;
        }
        UFS S(n);
        puts(S.Connected(0,n+1) ? "Yes" : "No");
    }
    return 0;
}

/*想象一下我打完这篇文章后把文中所有的「点」一个个改成「球」*/
NOIP2017唯一AC的一道题啊。

转载于:https://www.cnblogs.com/Capella/p/7978928.html

相关文章:

  • 如何走上更高平台分享传递干货知识:(开通个人Github面向开源及私有软件项目的托管平台:https://github.com/zlslch/)(图文详解)(博主推荐)...
  • H.264基础知识总结
  • Msys2的安装,并整合到cmder中
  • cron
  • 参数添加 dynamo
  • Oracle初级——续续篇
  • transient关键字
  • [codeforces] 25E Test || hash
  • python入门----hello world
  • HDU2019数列有序!
  • Kafka无消息丢失配置
  • 人体的数学美思考
  • winfrom 水晶报表制作
  • 洛谷 P1454 圣诞夜的极光
  • 关于手势处理
  • Angular4 模板式表单用法以及验证
  • download使用浅析
  • idea + plantuml 画流程图
  • Java 23种设计模式 之单例模式 7种实现方式
  • Java|序列化异常StreamCorruptedException的解决方法
  • Java超时控制的实现
  • laravel5.5 视图共享数据
  • PHP 小技巧
  • SpringBoot 实战 (三) | 配置文件详解
  • 闭包--闭包之tab栏切换(四)
  • 判断客户端类型,Android,iOS,PC
  • 前端路由实现-history
  • 前端性能优化——回流与重绘
  • 由插件封装引出的一丢丢思考
  • 在Mac OS X上安装 Ruby运行环境
  • 在weex里面使用chart图表
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​Spring Boot 分片上传文件
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (Oracle)SQL优化技巧(一):分页查询
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .Net IOC框架入门之一 Unity
  • .NET 设计一套高性能的弱事件机制
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • .NET中的十进制浮点类型,徐汇区网站设计
  • /etc/skel 目录作用
  • []sim300 GPRS数据收发程序
  • []新浪博客如何插入代码(其他博客应该也可以)
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [Android]通过PhoneLookup读取所有电话号码
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [GYCTF2020]Ez_Express