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

平面最近點對(加強版)

可能 一個 stream opera turn names main 中一 int

傳送門

ovo原來簡單版的非常的好做,只要肆意暴力枚舉即可。

不過這道題的數據範圍變成了200000,即使是洛谷神機也跑不過去的。

於是乎我們考慮分治法。

對於一個平面上的所有點,我們設其屬於一個點集S。我們想要把點集S盡可能平均的分成兩個點集,那麽我們只要每次取當前點集中所有點的中位數進行分割即可。

記錄dis表示一個點集之內兩點的最短距離。對於一個點集S,將其分解為兩個點集S1,S2之後,我們假設現在已經求出來S1的dis1,S2的dis2.那麽當前的答案d=min(dis1,dis2),之後如果在S1,S2中還有點對的距離<d,那麽一定分屬於兩個點集。

這個時候可以從中間的分割線分別向兩邊引出一條長度為d的矩形(記為C1,C2),能更新最近距離的點對一定分屬於這兩個矩形。然而單單這麽分析的話最壞的復雜度仍然可能非常大。那我們用dis的性質來考慮一下,對於C1中的每一個點k,能與之配對更新最短距離的,一定是C2中一個長為dis,高為2*dis的一個矩形之內的點。再者,因為S2中每兩個點的距離必須>=dis,所以這個矩形之內最多只可能有6個點。

也就是說對於C1中的每一個點k,最多只有6個點可以與之更新最短距離。那我們直接枚舉這六個點並且更新就可以了。

所以我們一開始先把所有的點按照橫坐標排序,先進行分割,之後分割到邊界之後進行回溯合並,合並的時候我們枚舉所以離中線的距離<=dis的點計入C1,C2,之後把這些點按照y值排序,之後進行配對更新即可,遇到不符合的情況要直接跳出,否則會T。(我就納悶為什麽照著人家寫能慢了一倍)

看一下跑的賊慢的代碼。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include
<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; typedef long long ll; const int M = 200005; const double INF = 9999999999; int n,L,f[M],dp[M]; int read() { int ans = 0
,op = 1; char ch = getchar(); while(ch < 0 || ch > 9) { if(ch == -) op = -1; ch = getchar(); } while(ch >=0 && ch <= 9) { ans *= 10; ans += ch - 0; ch = getchar(); } return ans * op; } struct node { double x,y; bool operator < (const node &g) const { if(x == g.x) return y < g.y; return x < g.x; } }s[M]; bool cmp(int a,int b) { return s[a].y < s[b].y; } double dist(int p,int q) { return sqrt((s[p].x - s[q].x)*(s[p].x - s[q].x) + (s[p].y - s[q].y) * (s[p].y - s[q].y)); } double merge(int l,int r) { double d = INF; if(l == r) return d; if(l+1 == r) return dist(l,r);//如果只有兩個點就返回其間的距離 int mid = (l+r) >> 1; double d1 = merge(l,mid); double d2 = merge(mid+1,r); d = min(d1,d2); int k = 0; rep(i,l,r) if(fabs(s[mid].x - s[i].x <= d)) f[++k] = i;//記錄所有離中線不超過dis的點 sort(f+1,f+1+k,cmp); rep(i,1,k) rep(j,i+1,k) { if(s[f[j]].y - s[f[i]].y >= d) break; double d3 = dist(f[i],f[j]); d = min(d,d3);//進行答案更新 } return d; } int main() { n = read(); rep(i,1,n) scanf("%lf %lf",&s[i].x,&s[i].y); sort(s+1,s+1+n); printf("%.4lf\n",merge(1,n)); return 0; }

還有dalao跑的快一倍的代碼。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=1000001 ;
const int INF=2<<20;
int n,temp[maxn];
struct Point
{
    double x,y;
}S[maxn];

bool cmp(const Point &a,const Point&b)
{
    if(a.x==b.x)
        return a.y<b.y;
    else
        return a.x<b.x;
}

bool cmps(const int &a,const int &b)
{
    return S[a].y<S[b].y ;
}

double min(double a,double b)
{
  return a<b?a:b;
}

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

double merge(int left,int right)
{
    double d=INF;
    if(left==right)
        return d ;
    if(left+1==right) 
        return dist(left,right);
    int mid=left+right>>1;
    double d1=merge(left,mid) ;
    double d2=merge(mid+1,right) ;
    d=min(d1,d2);
    int i,j,k=0;
    for(i=left;i<=right;i++)
        if(fabs(S[mid].x-S[i].x)<=d)
            temp[k++]=i;
    sort(temp,temp+k,cmps);
    for(i=0;i<k;i++)
        for(j=i+1;j<k&&S[temp[j]].y-S[temp[i]].y<d;j++)
        {
            double d3=dist(temp[i],temp[j]); 
            if(d>d3)
                d=d3;
        }
    return d;
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lf%lf",&S[i].x,&S[i].y);
    sort(S,S+n,cmp);
    return !printf("%.4lf\n",merge(0,n-1));
}

平面最近點對(加強版)