[Luogu 3958] NOIP2017 D2T1 奶酪

Posted Capella

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Luogu 3958] NOIP2017 D2T1 奶酪相关的知识,希望对你有一定的参考价值。

NOIP2017 D2T1 奶酪(Luogu 3958)


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



一个比较容易想到的搜索。我用的BFS。
因为涉及到开根,所以记得开double。
首先将所有的球按z值从小到大排序,如果最下方的球与底面相离,或是最上方的球与顶面相离,直接Pass。
接下来预处理,记得先初始化,对于每一组球(i,j),计算两球球心距离是否小于半径×2,用一个bool数组e[i][j]记录i能否到达j,避免BFS时重复计算。
我们会发现,可能不止一个球与底面相切或相交,也可能不止一个球与顶面相切或相交。
这就是说,BFS时起点和终点都可能不止一个,这给我们操作造成了一些麻烦(然而考场上我就这么硬搜的居然AC了)。
其实,通过建立「超级起点」和「超级终点」,可以把这个BFS变得正常。
我们可以用结构体数组的第0个元素表示「超级起点」,第n+1个元素表示「超级终点」。
预处理时,走一遍所有的球,如果当前球可以做起点,就连上当前球和超级起点,e[0][i]=e[i][0]=1;终点亦然。
在跑BFS的时候,对于每一个球,依次判断其能否到达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;
int T,n;
struct ball
{
    double x,y,z;
    bool operator <(const ball &rhs) const
    {
        return z<rhs.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()
{
    memset(vis,0,sizeof vis);//初始化
    memset(e,0,sizeof e);//初始化
    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;//i与j是否相连
}
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);
        for(int i=1;i<=n;++i)
            scanf("%lf %lf %lf",&s[i].x,&s[i].y,&s[i].z);
        sort(s+1,s+n+1);//排序
        if(s[1].z-r>0 || s[n].z+r<h)//直接Pass的情况
        {
            printf("No\n");
            continue;
        }
        Init();//初始化+预处理
        BFS();
        printf(vis[n+1]?"Yes\n":"No\n");//判断是否搜到
    }
    return 0;
}

NOIP2017唯一AC的一道题啊。

以上是关于[Luogu 3958] NOIP2017 D2T1 奶酪的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2015-D2T3运输计划

Luogu P3958 奶酪

Noip2017 感悟

noip2017普及 luogu3954 成绩

[luogu3959 noip2017] 宝藏 (状压dp)

[luogu P3960] [noip2017 d2t3] 队列