题意是平面上有一堆站点与士兵,要求士兵与站点最近的距离是多少。
本题为一道分治经典例题最近点对的问题的变形,需要添加条件是两点必须不属于同一集合即分属于站点与士兵。
求解最近点对问题的思路通常为分治。
不断地向下dfs,将原集合分为两份。现在点之间的距离便有了两种情况,第一种为同在一边可以进行递归处理。第二种为一个在左边,一个在右边。为了简化我们的时间复杂度,我们可以先算出这两边的最小值,将左右两边距离在这最小值之内的点存下来,进行暴力枚举计算距离,对于本题便可以AC。
下附AC代码。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define maxn 200005
using namespace std;
int n;
int temp[maxn];
struct p
{
double x,y,flag;
}pos[maxn];
double myabs(double now)
{
return now>=0 ? now :-now;
}
bool operator <(p x,p y)
{
if(x.x!=y.x)
return x.x<y.x;
return x.y<y.y;
}
double dis(int l,int r)
{
return sqrt((pos[l].x-pos[r].x )*(pos[l].x-pos[r].x)+( pos[l].y-pos[r].y)*( pos[l].y-pos[r].y));
}
bool cmp(int i,int j)
{
return pos[i].y<pos[j].y;
}
double dfs(int l,int r)
{
double now=98765432187654321;
if(l==r)
return now;
if(l+1==r)
{
if(pos[l].flag==pos[r].flag)
return now;
else
return dis(l,r);
}
int mid=(l+r)/2;
double ans1=dfs(l,mid);
double ans2=dfs(mid+1,r);
now=min(ans1,ans2);
int cnt=0;
for(int i=l;i<=r;i++)
{
if(myabs(pos[i].x-pos[mid].x)<now)
{
temp[++cnt]=i;
}
}
sort(temp+1,temp+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
for(int j=i+1;j<=cnt;j++)
{
if(myabs(pos[temp[i]].y-pos[temp[j]].y)>now)
break;
double d=dis(temp[i],temp[j]);
if(d<now && pos[temp[i]].flag!=pos[temp[j]].flag)
{
now=d;
}
}
return now;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
n*=2;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&pos[i].x,&pos[i].y);
if(i<=n/2)
pos[i].flag=1;
else
pos[i].flag=2;
}
sort(pos+1,pos+1+n);
printf("%.3lf\n",dfs(1,n));
}
}