[SWERC 2020] Safe Distance
题意
给定
N
N
N 个点与一个坐标
(
X
,
Y
)
(X,Y)
(X,Y),求从点
(
0
,
0
)
(0,0)
(0,0) 到点
(
X
,
Y
)
(X,Y)
(X,Y) 规划一条路线,不能走出
(
0
,
0
)
(0,0)
(0,0) 与
(
X
,
Y
)
(X,Y)
(X,Y) 间形成的矩形,使得通过这条路线时距离最近的点的距离最远,
N
≤
1000
N\le 1000
N≤1000。
思路
不用二分也可以。
如果答案为 x x x,那么以每个点为圆心,半径为 x x x 作圆后, ( 0 , 0 ) (0,0) (0,0) 与 ( X , Y ) (X,Y) (X,Y) 一定连通。
怎样连通不好想,可以想怎样不连通。可以发现,不连通是一定时一些相交或相切的圆把网格堵住了,于是我们可以将相交或相切的点放在同一个并查集里。想象每个圆都在不断变大,那么一定是圆心距离短的圆先相交或相切。我们给点两两建边,边权为两点间的距离,把边按照边权从小到大排序,每次将边的端点的并查集合并。因为左边界与下边界实际上是一个整体,上边界与右边界实际上是一个整体,所以如果某个边加完后使得左边界与下边界、上边界与右边界在一个并查集里,那么这个边权即为使得不连通的最小距离。答案理论上是这个最小距离减去极小的值,不过题目允许误差,输出这个值就可以。
代码
#include<bits/stdc++.h>
using namespace std;
int x,y,n,cnt,fa[1005],s[1005];
double a[1005],b[1005];
int f(int x){
if(fa[x]==x) return x;
return fa[x]=f(fa[x]);
}
struct node{
int u,v;
double w;
}bian[2000005];
bool cmp(node x,node y)
{
return x.w<y.w;
}
int main()
{
scanf("%d%d%d",&x,&y,&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&a[i],&b[i]);
for(int i=1;i<=n+2;i++) fa[i]=i,s[i]=1;
//n+1为左边界与下边界的整体,n+2为右边界与上边界的整体
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
bian[++cnt]={i,j,sqrt((a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]))/2};
//记得与上下左右边界连边
bian[++cnt]={i,n+1,min(x-a[i],b[i])};
bian[++cnt]={i,n+2,min(y-b[i],a[i])};
}
sort(bian+1,bian+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
{
if(s[f(bian[i].u)]<s[f(bian[i].v)])
s[f(bian[i].v)]+=s[bian[i].u],fa[f(bian[i].u)]=f(bian[i].v);
else
s[f(bian[i].u)]+=s[bian[i].v],fa[f(bian[i].v)]=f(bian[i].u);
if(f(n+1)==f(n+2))
{
printf("%.6f",bian[i].w);
return 0;
}
}
return 0;
}