洛谷P2498 [SDOI2012] 拯救小云公主 (二分)

最近在做二分的题目,个人认为这道题泰酷辣!!!便写下了这篇题解。

传送门

题目

题目大意

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

Ps:英雄走的方向是任意的,但是不能走出矩形的范围。即英雄可以到达矩形范围内的任意一个点(没有必要是整点)

输入格式

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

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

输出格式

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

样例输入 #1

1 3 3
2 2

样例输出 #1

1.00

提示

数据范围:

20%数据,boss坐标范围小于等于50;

60%数据,n<=1500;

100%数据,n<=3000;

解题思路

1.首先看到“最近距离最远”,这种词言一般是用二分,且该题是找最大值,所以符合check函数就往右找,此时可以直接套用浮点数二分模板;

2.接下来是写check函数:显然:(0, 0) 和 (row, line)是必须要在路径上的。假设boss的坐标是 (x, y) ,那么最短的距离是和点(1, y), (x, 1), (row, y), (x, line),此时的距离分别为x - 1, y - 1, row - x, line - y,此时到了关键点,是否只有四者有两个点符合情况即可?当然不是!我们需要让左上角的两个点的距离x - 1, line - y都合法或者右下角的两个距离(x, 1), (row, y)都合法就好了;

3.不仅可以通过边界到,还可以通过中间任意位置走过去,这时候就需要求出两个boss间的距离,两点之间直线最短,如果此时该直线的中点合法也是可以通过这条路的。

4.接下来是书写check函数:通过以上分析,边界只要有一条合法即可。如果左上有一个点不合法,那么这条路就不合法了,右下同理,如果双方不合法就GG~ 所以此时需要用一个队列来维护这个关系,我这里的判定条件是如果左上不合法就入队,右下还不合理那就下一位了。那如果有的boss可以通过左上直达,有的不可以呢?(其实就是多了一种情况,只是这种情况需要通过边框来进入内部,所以它需要和左上或右下绑定)这时候就需要遍历中间的n条路径来看是否存在合法了,这个工程量非常大,那怎么减小呢?很简单,只需判定两点的距离和判断的最近距离是否合法即可,如果不合法就入队,相当于一开始的左上两点,把最后的希望交给右下两点,至此就结束了

参考代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 3010;
const double eps=1e-6;
int n,row,col;
int x[N], y[N];
int dist[N][N];
bool st[N];

//两点之间的距离
int get(int i,int j)
{
    return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}

//判定距离和查找数的关系
bool able(int yy,double r)
{
    return (double)yy<4*r*r;
}

bool check(double xx)
{
    memset(st,0,sizeof st);
    queue<int> q;
    for(int i=1; i<=n; i++) 
        if(x[i]-1<xx || col-y[i]<xx) q.push(i), st[i]=1;
    while(q.size())
    {
        int p=q.front();
        q.pop();
        if(y[p]-1<xx || row-x[p]<xx) return false;
        for(int i=1; i<=n; i++)
            if(!st[i] && able(dist[p][i], xx))
                st[i]=1, q.push(i);
    }
    return true;
}

int main()
{
    cin>>n>>row>>col;
    for(int i=1; i<=n; i++) cin>>x[i]>>y[i];
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            dist[i][j]=get(i,j);
    double l=0, r=min(row,col);
    for(int i=1; i<=70; i++)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf", l);
    return 0;
}

完结撒花~~~~~~
谢谢支持!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_谦言万语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值