题意
给两个点集,求两个点集中任意两点的最小距离。
题解
分治+标记
如果会了最近点对问题,就可以做了。
只要在匹配时注意一下不同集合才能匹配就可以了。
我用key做标记:key=1、3表示在集合A,key=2、4在集合B;其中key=1、2表示在a[mid]的左边,key=3、4表示在右边。所以1要和4匹配,2和3匹配。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double inf=1<<30;
const int maxn=200010;
double dis(double x1,double y1,double x2,double y2)
{
return sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
}
int n;
struct node
{
double x,y;
int key;
}a[maxn],b[maxn];int bn;
bool cmpx(node a1,node a2)
{
return a1.x<a2.x;
}
bool cmpy(node b1,node b2)
{
return b1.y<b2.y;
}
double solve(int l,int r)//求第l~r个点中的最近点对
{
if(l==r) return inf;
int mid=l+r>>1;
double d=min(solve(l,mid),solve(mid+1,r));
bn=0;
for(int i=mid;i>=l && a[mid].x-a[i].x<=d;i--) b[++bn]=a[i];//key->1,2
for(int i=mid+1;i<=r && a[i].x-a[mid].x<=d;i++) b[++bn]=a[i],b[bn].key+=2;//key->3,4
sort(b+1,b+bn+1,cmpy);
for(int i=1;i<bn;i++)
{
for(int j=i+1;j<=bn;j++)
{
if( b[i].key==1&&b[j].key==4 || b[i].key==4&&b[j].key==1 ||
b[i].key==2&&b[j].key==3 || b[i].key==3&&b[j].key==2 )
{
if(b[j].y-b[i].y>=d) break;
d=min(d,dis(b[i].x,b[i].y,b[j].x,b[j].y));
}
}
}
return d;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y),a[i].key=1;
n*=2;
for(int i=n/2+1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y),a[i].key=2;
sort(a+1,a+n+1,cmpx);
double ans=solve(1,n);
printf("%.3lf\n",ans);
}
return 0;
}