给出两个点集,然后求两个点集之间的最近距离。
思路:开始用的旋转卡壳,两个点集先求凸包,这样就变成了两个凸包间最近距离,但是死活TLE,然后就换了平面最近点对来做,把两个点集标记一下,判断下是不是在同一个点集里就行了
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int N=200002;
const double EPS=1e-10;
const double INF=1e20;
struct Point
{
double x,y;
bool flag;
} p[N];
int n,o[N],on;
inline double dis(const Point & a,const Point & b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline int dcmp(double a,double b)
{
if(a-b<EPS && b-a<EPS) return 0;
if(a>b)
return 1;
return -1;
}
inline bool cmp_x(const Point & a,const Point & b)
{
return dcmp(a.x,b.x)<0;
}
inline bool cmp_y(const int & a,const int & b)
{
return dcmp(p[a].y,p[b].y)<0;
}
inline double min(double a,double b)
{
return a<b ? a : b;
}
double search(int s,int t,Point& x,Point& y)
{
int mid=(s+t)>>1,i,j;
double getRet,getDis,ret=INF;
Point getx,gety;
if(s>=t) return ret;
for(i=mid;i>=s && ! dcmp(p[i].x,p[mid].x);--i);
ret=search(s,i,x,y);
for(i=mid;i<=t && ! dcmp(p[i].x,p[mid].x);++i);
getRet=search(i,t,getx,gety);
if(getRet<ret)
{
ret=getRet;
x=getx;
y=gety;
}
on=0;
for(i=mid;i>=s && dcmp(p[mid].x-p[i].x,ret)<=0;--i)
o[++on]=i;
for(i=mid+1;i<=t && dcmp(p[i].x-p[mid].x,ret)<=0;++i)
o[++on]=i;
sort(o+1,o+on+1,cmp_y);
for(i=1;i<=on;++i)
{
for(j=1;j<=10;++j)
{
if(p[o[i]].flag==p[o[i+j]].flag)
continue;
getDis=dis(p[o[i]],p[o[i+j]]);
if(i+j<=on&&getDis<ret)
{
ret=getDis;
x=p[o[i]];
y=p[o[i+j]];
}
}
}
return ret;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].flag=0;
}
for(int i=n+1;i<=2*n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].flag=1;
}
n=2*n;
sort(p+1,p+n+1,cmp_x);
Point x,y;
double minDis=search(1,n,x,y);
printf("%.3lf\n",minDis);
}
return 0;
}