平面最近点对问题,经典分治问题。
按照点的x坐标排序做预处理。
点集合s按照x坐标平分为l,r两组点,分别得到每组组内点的最小距离dl和dr,d = min(dl,dr),然后记横坐标距分割线距离小于d的点集为r,计算r内点与点的距离,与d比较,得到s内点的最小距离。
细节再与计算r内点与点的距离。
再从l组和r组得到分治结果后,对l和r内的点对y进行归并排序(复杂度为n),并筛查出属于r的点,这个排序后的序列记为R。这时候,有个trick。
任意一个点p,只要和他再R内的之前6个,之后6个点计算距离并比较,就足够了。证明参见《算法设计与分析》(屈婉玲)。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<cstring>
using namespace std;
struct point{
int x,y;
int idx;
};
point temp[201000];
double distance(point a, point b)
{
return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}
double min_dis(point a[], point aux[], int l, int r)
{
double delta = 1e10 * 2e10;
if (l == r){
// printf("%d %d %lf\n", l, r, delta);
return delta;
}
int mid = (l + r)/2;
delta = min(min(delta, min_dis(a, aux, l, mid)), min_dis(a, aux, mid + 1, r));
int lp = l, rp = mid + 1, auxp = l;
while (lp <= mid && rp <= r){
if (a[lp].y <= a[rp].y){
aux[auxp ++] = a[lp ++];
}
else{
aux[auxp++] = a[rp ++];
}
}
while (lp <= mid){
aux[auxp ++] = a[lp ++];
}
while (rp <= r){
aux[auxp ++] = a[rp ++];
}
int t = 0;
for (int i = l; i <= r; i ++){
if (abs(aux[i].x - a[mid].x) < delta){
temp[t ++] = aux[i];
}
}
for (int i = 0; i < t; i ++){
for (int j = 1; j <= 6 && (i + j) < t; j ++){
delta = min(delta, distance(temp[i], temp[i + j]));
}
for (int j = -1; j >= -6 && (i + j ) >= 0; j --){
delta = min(delta, distance(temp[i], temp[i + j]));
}
}
memcpy(&a[l], &aux[l], sizeof(point) * (r - l + 1));
// printf("%d %d %lf\n", l, r, delta);
return delta;
}
int main(){
int n;
struct point a[201000] = {}, aux[201000] = {};
while (cin >> n){
for (int i = 0; i < n; i ++){
scanf("%d %d", &a[i].x, &a[i].y);
}
sort(a, a + n, [](point p1, point p2)->bool {return p1.y < p2. y;});
for (int i = 0; i < n; i ++){
a[i].idx = i;
}
double dis = min_dis(a, aux, 0, n - 1);
printf("%.3lf", dis);
}
return 0;
}