平面最近点对

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define rint register int
#define ll long long
using namespace std;
template<typename xxx>inline void read(xxx &x)
{
    x=0;int f=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x*=f;
}
template<typename xxx>inline void print(xxx x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>=10) print(x/10);
    putchar(x%10+48);
}
const int maxn=2000010;
const int inf=2e9;
int n,ans[maxn];
struct node{
    double x,y;
}e[maxn];
inline bool cmp1(node a,node b)
{
    if(a.x==b.x) return a.y<b.y;
    else return a.x<b.x;
}
inline bool cmp2(int a,int b)
{
    return e[a].y<e[b].y;
}
inline double calc(int a,int b)
{
    return sqrt((e[a].x-e[b].x)*(e[a].x-e[b].x)+(e[a].y-e[b].y)*(e[a].y-e[b].y));
}
inline double merge(int l,int r)
{
    double d=9999999999.0;int k=0;
    if(l==r) return d;
    if(l+1==r) return calc(l,r);
    int mid=(l+r)>>1;
    double d1=merge(l,mid);
    double d2=merge(mid+1,r);
    d=min(d1,d2);
    for(rint i=l;i<=r;++i) if(fabs(e[mid].x-e[i].x)<=d) ans[++k]=i;
    stable_sort(ans+1,ans+k+1,cmp2);
    for(rint i=1;i<=k;++i)
    {
        for(rint j=i+1;j<=k && e[ans[j]].y-e[ans[i]].y<d;++j)
        {
            double dis=calc(ans[i],ans[j]);
            if(dis<d) d=dis;
        }
    }
    return d;
}
int main()
{
    read(n);
    for(rint i=1;i<=n;++i) scanf("%lf%lf",&e[i].x,&e[i].y);
    stable_sort(e+1,e+n+1,cmp1);
    printf("%.4lf",merge(1,n));
}
/*神仙算法
我们充分发扬人类智慧:

将所有点全部绕原点旋转同一个角度,然后按xx坐标排序

根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远

所以我们只取每个点向后的5个点来计算答案

这样速度快得飞起,在n=1000000n=1000000时都可以在1s内卡过

 #include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<ctime> 
#define INF 9999999999.0
using namespace std;

struct node{
    double x,y;
}a[2000010];
int n;
double ans=INF;             //初始化答案为一个很大的数 

bool cmp(node a,node b){
    return a.x<b.x;         //按照横坐标排序 
}
double len(node a,node b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));//计算两点之间距离 
}
void calc(){
    for (int i=1;i<=n;i++){
        for (int j=i+1;j<=i+5;j++){
            double temp;
            temp=len(a[i],a[j]);
            ans=min(ans,temp);
        }
    }
}
void around(int ds){
    for (int i=1;i<=n;i++){
        double x=a[i].x,y=a[i].y;//旋转前的点 
        double xn,yn;            //旋转后的点 
        double xyu=0.0,yyu=0.0;  //原点 
        xn= (x - xyu)*cos(ds) - (y - yyu)*sin(ds) + xyu ;
        yn= (x - xyu)*sin(ds) + (y - yyu)*cos(ds) + yyu ;
        a[i].x=xn;
        a[i].y=yn;
    }
    sort(a+1,a+1+n,cmp);        //顺便排序 
    calc();                     //顺便计算 
}

int main(){
    srand(time(NULL));          //随机数种子 
    for (int i=1;i<=200009;i++){
        a[i].x=INF;a[i].y=INF;  //初始化 
    }

    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%lf%lf",&a[i].x,&a[i].y);
    }
    around(0);                  //将原图像进行排序并枚举每个点与其后五个点比较 
    around(rand()%360);         //将图像随机(0°~359°)旋转  并排序 并计算 
    around(rand()%360);         //将图像随机(0°~359°)旋转  并排序 并计算 
    printf("%.4lf",ans);
    return 0;
}
*/ 
原文地址:https://www.cnblogs.com/Thomastine/p/11862866.html