[SWERC 2020] Safe Distance
题意
给定 NNN 个点与一个坐标 (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≤1000N\le 1000N≤1000。
思路
不用二分也可以。
如果答案为 xxx,那么以每个点为圆心,半径为 xxx 作圆后,(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);elses[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;
}