[SWERC 2020] Safe Distance题解

[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 N1000
在这里插入图片描述

思路

不用二分也可以。

如果答案为 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值