题型:计算几何
题意:
有A和B两个点集,求min(dis(x,y)),x⊂A,y⊂B。
分析:
平面最近点集的小变形。将同一个集合的点的距离设置为INF之后直接套平面最近点对即可。
关于平面最近点对的学习,算法设计与分析上有详解,也可以参考:
http://yzmduncan.iteye.com/blog/1432880
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 123456
using namespace std;
const double INF = 1e20;
struct Point {
double x;
double y;
bool flag;
} point[MAXN*2];
int n;
int tmpt[MAXN*2];
bool cmpxy(const Point& a ,const Point& b) {
if(a.x !=b.x)
return a.x<b.x;
return a.y<b.y;
}
bool cmpy(const int& a ,const int& b) {
return point[a].y < point[b].y;
}
double min(double a,double b) {
return a<b?a:b;
}
double dis(int i,int j) {
if(point[i].flag == point[j].flag) return INF;
return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
}
double Closest_Pair(int left,int right) {
double d = INF;
if(left == right)
return d;
if(left+1 == right)
return dis(left,right);
int mid = (left+right)>>1;
double d1 = Closest_Pair(left,mid);
double d2 = Closest_Pair(mid+1,right);
d = min(d1,d2);
int i,j,k=0;
//分理出宽度为d的区间
for(i=left; i<=right; i++) {
if(fabs(point[mid].x-point[i].x) <= d)
tmpt[k++] = i;
}
sort(tmpt,tmpt+k,cmpy);
//线性扫描
for(i=0; i<k; i++) {
for(j=i+1; j<k&&point[tmpt[j]].y-point[tmpt[i]].y<d; j++) {
double d3 = dis(tmpt[i],tmpt[j]);
if(d>d3) d = d3;
}
}
return d;
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
for(int i=0; i<n; i++) {
scanf("%lf %lf",&point[i].x,&point[i].y);
point[i].flag = false;
}
for(int i=n; i<n*2; i++) {
scanf("%lf %lf",&point[i].x,&point[i].y);
point[i].flag = true;
}
sort(point,point+2*n,cmpxy);
printf("%.3f\n",Closest_Pair(0,2*n-1));
}
return 0;
}