1. 程式人生 > >Luogu P1429 平面最近點對(加強版)

Luogu P1429 平面最近點對(加強版)

P1429 平面最近點對(加強版)

題意

題目描述

給定平面上\(n\)個點,找出其中的一對點的距離,使得在這\(n\)個點的所有點對中,該距離為所有點對中最小的。

輸入輸出格式

輸入格式:

第一行:\(n\)\(2\leq n\leq 200000\)

接下來\(n\)行:每行兩個實數:\(x\ y\),表示一個點的行座標和列座標,中間用一個空格隔開。

輸出格式:

僅一行,一個實數,表示最短距離,精確到小數點後面\(4\)位。

輸入輸出樣例

輸入樣例#1:

3
1 1
1 2
2 2

輸出樣例#1:

1.0000

說明

\(0\leq x,y\leq 10^9\)

思路

利用分治的方法實現。我們先把所有點按照橫座標排序,然後查詢每一個區間的最近點對距離。假設當前查詢的是\([l,r]\)

區間的最近點對距離,那麼這個區間的答案就在\([l,mid]\)區間的最近點對距離、\([mid+1,r]\)區間的最近點對距離、靠近中間的分別在兩個區間中的一些點之間的距離中產生,我們主要考慮第三部分答案如何統計。

首先通過分治,我們已經求出了左右兩區間的最近點對距離\(min\),接下來找到\([l,r]\)區間內橫座標與\(mid\)的橫座標相差不超過\(min\)的點,並將這些點兩兩匹配求出最近距離。這樣能保證答案的正確性,但是時間複雜度呢?據說這樣子做的時間複雜度是\(O(n\log n)\)的,所以也不用擔心超時的問題。

AC程式碼

#include<bits/stdc++.h>
using namespace std;
int n;
struct Point
{
    double x,y;
    bool operator < (const Point &sjf){return x<sjf.x;}
}p[200005],q[200005];
double devide(int l,int r)
{
    if(l==r) return DBL_MAX;
    else if(l+1==r) return sqrt((p[l].x-p[r].x)*(p[l].x-p[r].x)+(p[l].y-p[r].y)*(p[l].y-p[r].y));
    int mid=(l+r)>>1,cnt=0;
    double d=min(devide(l,mid),devide(mid+1,r));
    for(int i=l;i<=r;i++) if(fabs(p[i].x-p[mid].x)<=d) q[++cnt]=p[i];
    for(int i=1;i<=cnt;i++)
        for(int j=i+1;j<=cnt&&fabs(q[i].x-q[j].x)<=d;j++)
            d=min(d,sqrt((q[i].x-q[j].x)*(q[i].x-q[j].x)+(q[i].y-q[j].y)*(q[i].y-q[j].y)));
    return d;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
    sort(p+1,p+n+1);
    printf("%.4f",devide(1,n));
    return 0;
}