平面最近点对

简介

平面最近点对问题即求一个平面上的 \(n\) 个点中距离最短的一对点,朴素的做法是双重循环枚举每一对点,时间复杂度为 \(O(n^2)\) ,利用归并排序的分治思想,可以将复杂度降为 \(O(n\log n)\)

原理

先将所有点按 \(x\) 坐标排序,这样我们就可以把点分成两部分,一部分在 \(mid\) 左边,另一部分在右边

那么最近点对只有三种情况:两点都在 \(mid\) 左边/两点都在 \(mid\) 右边/一点在左一点在右,前两种情况的答案可以继续分治递归得到,记为 \(d\) ,所以我们只要考虑如何求出第三种情况的最近点对 \(d_1\) ,那么最终答案就是 \(\min(d,d_1)\)

一个自然的思路是筛选出左右区间中 \(x\) 坐标距离 \(mid\) 小于 \(d\)\(y\) 坐标距离小于 \(d\) 的点,因为只有这些点形成的点对距离才有可能小于 \(d\) ,再枚举这些点中的点对更新答案

这一步还有一个很强的性质:每处理一个左边的点时,右边最多只有 \(6\) 个点会被考虑到

证明:假设有 \(7\) 个点在符合范围要求,根据抽屉原理,一定会有两个点在下图的同一个小矩形中

由于这两点在同一个矩形,那么它们的距离一定小于该矩形对角线的长度,即为 \(r\sqrt{(\frac{2}{3})^2+(\frac{1}{2})^2}=\frac{5}{6}r<r=d\) 那么就不满足 \(d\) 是左区间点对的最小距离了

在归并分治的部分,我们按 \(y\) 坐标对区间内的点排序,这样最后枚举 \(y\) 坐标距离 \(mid\) 小于 \(d\) 的点时遇到不符合条件的点可以直接 break

每层分治的复杂度为 \(O(n)\) ,层数是 \(O(\log n)\) ,总复杂度是 \(O(n\log n)\)

代码

Luogu P7883

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAX_N = 400000 + 5;
const ll INF = 1e18;
int n;
struct node {
    ll x, y;
} a[MAX_N], b[MAX_N], tmp[MAX_N];

bool cmp(node n1, node n2)
{
    return n1.x < n2.x;
}

ll dis(node n1, node n2)
{
    return (n1.x - n2.x) * (n1.x - n2.x) + (n1.y - n2.y) * (n1.y - n2.y);
}

ll msort(int l, int r)
{
    if(l == r)
        return INF;
    int mid = (l + r) >> 1;
    ll val = a[mid].x;
    ll d = min(msort(l, mid), msort(mid + 1, r));
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++) {
        if(j > r || (i <= mid && a[i].y < a[j].y))
            b[k] = a[i++];
        else
            b[k] = a[j++];
    }
    for(int k = l; k <= r; k++)
        a[k] = b[k];
    int t = 0;
    for(int k = l; k <= r; k++)
        if((a[k].x - val) * (a[k].x - val) < d)
            tmp[++t] = a[k];
    for(int i = 1; i <= t; i++)
        for(int j = i + 1; j <= t && (tmp[i].y - tmp[j].y) * (tmp[i].y - tmp[j].y) < d; j++)
            d = min(d, dis(tmp[i], tmp[j]));
    return d;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lld%lld", &a[i].x, &a[i].y);
    sort(a + 1, a + n + 1, cmp);
    printf("%lld\n", msort(1, n));
    return 0;
}
posted @ 2022-02-05 23:22  f(k(t))  阅读(436)  评论(0编辑  收藏  举报