二分+并查集【bzoj3007】[SDOI2012]拯救小云公主

Description

英雄又即将踏上拯救公主的道路……

这次的拯救目标是——爱和正义的小云公主。

英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss。当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务。

但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻。

Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line)。英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离最远。

Ps:英雄走的方向是任意的。

你可以帮帮他吗?

当英雄找到了美丽漂亮的小云公主,立刻就被boss包围了!!!英雄缓闭双眼,举手轻挥,白光一闪后使用了回城卷轴,回到了城堡,但只有小云公主回去了……因为英雄忘了进入回城的法阵了。

Input

第一行,输入三个整数,n表示boss的数目,row,line表示矩形的大小;

接下来n行,每行分别两个整数表示boss的位置坐标。

Output

输出一个小数,表示英雄的路径离boss的最远距离,精确到小数点后两位。

这里的距离指的是欧几里德距离

首先很容易看出是二分答案

我们可以看成是以每个\(boss\)为圆心作一个半径为\(r\)的圆,我们想要求的就是让这些圆尽可能大,并且不能影响我们从\((1,1)\)\((n,m)\)。(不能覆盖)

直接考虑边界条件\((n,1)\)\((1,m)\)如果这两个点没有被覆盖,那我必然可以到达\((n,m)\)

PS:这里的判断条件不是同时判断。

这样用\(||\)判断,可以达到我们边界不被封锁的情况。

并查集维护连通即可。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define eps 1e-4
#define R register

using namespace std;

const int gz=3e3+8;

inline void in(R int &x)
{
    R int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int nn,n,m,f[gz];

struct cod
{
    int x,y;
}bos[gz];

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

inline double xx(R double x)
{
    return x*x;
}

inline double dis(R int i,R int j)
{
    return xx(bos[i].x-bos[j].x)+xx(bos[i].y-bos[j].y);
}

inline bool ok(R double r)
{
    for(R int i=0;i<=nn+1;i++)f[i]=i;
    for(R int i=1;i<=nn;i++)
    {
        for(R int j=1;j<i;j++)
        {
            if(dis(i,j)<=xx(2*r))
            {
                R int fa=find(i),fb=find(j);
                if(fa!=fb)f[fa]=fb;
            }
        }
        if(bos[i].x-r<=1 or bos[i].y+r>=m)
        {
            R int fa=find(i),fb=find(0);
            if(fa!=fb)f[fa]=fb;
        }
        if(bos[i].x+r>=n or bos[i].y-r<=1)
        {
            R int fa=find(i),fb=find(nn+1);
            if(fa!=fb)f[fa]=fb;
        }
    }
    return find(0)!=find(nn+1);
}

int main()
{
    in(nn),in(n),in(m);
    for(R int i=1;i<=nn;i++)
        in(bos[i].x),in(bos[i].y);
    R double ll=0,rr=min(n,m);
    while(fabs(ll-rr)>eps)
    {
        R double mid=(ll+rr)/2;
        if(ok(mid))ll=mid;
        else rr=mid;
    }
    printf("%.2f",ll);
}

转载于:https://www.cnblogs.com/-guz/p/9975749.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值