最近在做二分的题目,个人认为这道题泰酷辣!!!便写下了这篇题解。
传送门
Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line)。英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离最远。
Ps:英雄走的方向是任意的,但是不能走出矩形的范围。即英雄可以到达矩形范围内的任意一个点(没有必要是整点)
第一行,输入三个整数,n表示boss的数目,row,line表示矩形的大小;
接下来n行,每行分别两个整数表示boss的位置坐标。
输出一个小数,表示英雄的路径离boss的最远距离,精确到小数点后两位。
1 3 3
2 2
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
#include
#include
#include
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;
}
完结撒花~~~~~~
谢谢支持!