平面最近点对

题目描述

给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的

输入输出格式

输入格式:

第一行:n;2≤n≤200000

接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。

输出格式:

仅一行,一个实数,表示最短距离,精确到小数点后面4位。

输入输出样例

输入样例#1: 
3
1 1
1 2
2 2
输出样例#1: 
1.0000

说明

0<=x,y<=10^9


用分治解决这个问题。

我们正在计算区间$[L, R]$内的答案,假设我们已经计算出了$[L, mid]$和$[mid+1,R]$的答案,我们要求出$[L,R]$答案。

我们设$[L, mid]$的答案是$d1$,$[mid+1,R]$的答案是$d2$,那么区间[L,R]的答案$d=min(d1, d2)$。

这是显然不对的。

为了方便,一下用$S1$代指$[L, mid]$区间,用$S2$代指$[mid+1,R]$区间。

因为我们只考虑了两个区间内部点的答案,跨区间的两个点有可能成为答案。

我们设一个在$S1$中的点为$p1$,在$S2$中的点为$p2$。

那么如果我们暴力枚举,就又成$O(N^2)$的了。

但我们发现,我们现在的答案已经是$d$了,所以我们根本没必要枚举不可能成为答案的点。

假如区间中点是$mid$。

那有用的点其实都在$[mid-d, mid+d]$之间,但是这样最坏情况下还是$O(N^2)$的。

我们又发现,对于一个点$p1$,可能成为答案的$p2$的$y$坐标的范围,又在$[y[p1]-d, y[p1]+d]$之间。

对于每个点$p1$,这样的区间是$d imes 2d$这么大的。

因为我们求出最小距离是$d$,那么在这个有效的区间内最多只有6个点,这个可以画图感受一下。

所以统计跨越区间的贡献的复杂度变成了线性。

因为每次分治还要排序,所以总体复杂度是$O(Nlog_2N)$的。

好像可以边分治边归并,可以去掉一个log,不太了解。


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define reg register 
#define gc getchar
inline int read() {
    int res=0;char ch=gc();bool fu=0;
    while(!isdigit(ch)){if(ch=='-')fu=1;ch=gc();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc();
    return fu?-res:res;
}

int n;
struct date {
    double x, y;
}da[200005];
int tmp[200005];

bool cmp1(date a, date b) {
    return a.x < b.x;
}

bool cmp2(int a, int b) {
    return da[a].y < da[b].y;
}

inline double dist(int i, int j) {
    return sqrt((da[i].x - da[j].x) * (da[i].x - da[j].x) + (da[i].y - da[j].y) * (da[i].y - da[j].y));
}

double ans;

double Solve(int L, int R)
{
    double d = 1e20 * 1.0;
    if (L == R) return d;
    if (L + 1 == R) return dist(L, R);
    int mid = (L + R) >> 1;
    d = min(Solve(L, mid), Solve(mid + 1, R));
    int cnt = 0;
    for (reg int i = L ; i <= R ; i ++) 
        if (fabs(da[i].x - da[mid].x) <= d) tmp[++cnt] = i;
    sort(tmp + 1, tmp + 1 + cnt, cmp2);
    for (reg int i = 1 ; i <= cnt ; i ++)
        for (reg int j = i + 1 ; j <= cnt and da[tmp[j]].y - da[tmp[i]].y <= d ; j ++)
            d = min(d, dist(tmp[i], tmp[j]));
    return d;
}

int main()
{
    n = read();
    for (reg int i = 1 ; i <= n ; i ++) scanf("%lf%lf", &da[i].x,&da[i].y);
    sort(da + 1, da + 1 + n, cmp1);
    printf("%.4lf
", Solve(1, n));
    return 0;
}
原文地址:https://www.cnblogs.com/BriMon/p/9786507.html