【模板】平面最近点对-分治

传送门:洛谷-平面最近点对


题意

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


数据范围

2≤n≤200000


题解

一、分治
按x排序,左右分治,边界处理一下就好了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath> 
#define db double
const int INF=0x7fffffff;
using namespace std;
typedef long long ll;
const int N=2e5+10;
struct P{
    int x,y;
}p[N];
int n,in[N],cnt;
inline db sqr(int x){return (db)x*(db)x;}
inline db fab(db x){return x<0 ? -x:x;}
inline db min(db a,db b){return a<b? a:b;}
inline db dist(P a,P b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
bool cmp(const P &a,const P &b){
   if(a.x==b.x){
      return a.y<b.y;
   }
   return a.x<b.x;
}
bool cmq(const int &a,const int &b){return p[a].y<p[b].y;}

inline db merge(int l,int r)
{
    db d=INF;
    if(l==r) return d;
    if(l+1==r) return dist(p[l],p[r]);
    int mid=(l+r)>>1;
    db pa=merge(l,mid);
    db pb=merge(mid+1,r);
    d=min(pa,pb);   
    cnt=0;
    for(int i=l;i<=r;i++){
        if(fab(p[i].x-p[mid].x)<d){//<=
           in[++cnt]=i;
        }
    }
    sort(in+1,in+cnt+1,cmq);
    for(int i=1;i<=cnt;i++){
        for(int j=i+1;j<=cnt && p[in[j]].y-p[in[i]].y<d;j++){
            db now=dist(p[in[i]],p[in[j]]);
            d=min(now,d);
        }
    }
    return d;
}

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

二、扫描
按y排序,从上往下扫,优化看代码。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath> 
#define db double
const int INF=0x7fffffff;
using namespace std;
typedef long long ll;
const int N=2e5+10;
struct P{
    int x,y;
}p[N];

db ans;
int n,head[N],to[N],nxt[N],cnt,tot;
inline db sqr(int x){return (db)x*(db)x;}
inline db fab(db x){return x<0 ? -x:x;}
inline db min(db a,db b){return a<b? a:b;}
inline db dist(P a,P b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
inline void lk(int u,int v)
{
    nxt[++tot]=head[u];head[u]=tot;to[tot]=v;
}

inline bool cmq(const P &a,const P &b){
    if(a.y==b.y) return a.x<b.x; 
    return a.y<b.y;
}

int main(){
    ans=INF;
    scanf("%d",&n); 
    for(int i=1;i<=n;i++){
       scanf("%d%d",&p[i].x,&p[i].y);
    }
    sort(p+1,p+n+1,cmq);
    lk(1,1);++cnt;
    for(int i=2;i<=n;i++){
        if(p[i].y!=p[i-1].y){
            ++cnt;
        }else{
            ans=min(ans,(db)(p[i].x-p[i-1].x));
        }
        lk(cnt,i);
    }
    for(int i=1;i<cnt;i++){
        int mx=i+1;
        while(mx<=cnt && (db)(p[to[head[mx]]].y-p[to[head[i]]].y)<=ans){
            int qw=head[mx];
            for(int e,k=head[i];k && qw ;k=nxt[k]){
                int ty=to[k];
                for(;qw;qw=nxt[qw]){
                    while(nxt[qw]!=0 && p[to[nxt[qw]]].x>=p[ty].x){
                        qw=nxt[qw];
                    }
                    e=to[qw];
                    if(p[e].x<=p[ty].x){
                        ans=min(dist(p[e],p[ty]),ans);
                        break;
                    }
                    ans=min(dist(p[e],p[ty]),ans);
                }
            }
            mx++; 
        }
    }
    printf("%.4lf\n",ans);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值