平面最近点对 n^2和nlogn做法 计算几何

题目描述:

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

一、暴力n平方做法

      既然是暴力做法,那肯定是非常非常暴力的做法,无疑是枚举。枚举i从1~n,j从i+1~n,用距离公式计算出第i个点和第j个点的距离,然后找到最小值即可。放一道模板题吧!

AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
ll n,mn;
ll x[10005],y[10005],now;
double ans;
inline ll dis(ll x1,ll y1,ll x2,ll y2){
	return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
int main(){
	freopen("a.in","r",stdin);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&x[i],&y[i]);
	}
	mn=dis(x[1],y[1],x[2],y[2]);
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			now=dis(x[i],y[i],x[j],y[j]);
			if(mn>now){
				mn=now;
			}
		} 
	}
	printf("%.4lf",sqrt(mn));
	return 0;
}

二、分治nlogn做法

    有时候题目的数据范围超过了暴力能接受的范围怎么办呢?这时我们考虑一种分治算法。我们先将所有点按x坐标升序排序一遍。然后考虑分治,对于当前要处理的[l,r]区间内的所有点(若l==r返回INF),我们将其按照横坐标均分为一半对一半,并在他们直接划出一条分界线。左右两边各自算出其包含范围内的最小值,然后怎么合并两边的点呢?我们算出左右两边的最小值分别为d1 , d2(递归处理)。我们再设一个d=min(d1,d2)。很明显,左右两边的点只有到分界线距离在d以内才有机会更新最小值,而且我们还能发现这样的点不超过8个,可以很大程度缩小搜索范围。于是我们就在很短的时间内合并成功了!这种做法也丢一道模板题吧!

AC代码:


#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int Maxn=200005;
struct node{
    ll x,y;
}a[Maxn];
int n,tmp[Maxn];ll inf;
inline ll dis(node s1,node s2){
    return (s1.x-s2.x)*(s1.x-s2.x)+(s1.y-s2.y)*(s1.y-s2.y);
}
inline ll mn(ll x,ll y){
    return x<y?x:y;
}
inline ll ab(ll x){
    return x>0?x:-x;
}
bool cmp(node s1,node s2){
    if(s1.x!=s2.x)return s1.x<s2.x;
    return s1.y<s2.y;
}
ll merge(int l,int r){
    ll d=inf;
    if(l==r){
        return d;
    }
    if(l+1==r){
        return dis(a[l],a[r]);
    }
    int mid=l+r>>1,k=0;
    ll d1=merge(l,mid);
	ll d2=merge(mid+1,r),dt;
    d=mn(d1,d2);
    for(int i=l;i<=r;i++){
    	dt=a[mid].x-a[i].x;
        if(dt*dt<=d){
            tmp[++k]=i;
        }
    }
    for(int i=1;i<=k;i++){
        for(int j=i+1;j<=k;j++){
            d=mn(d,dis(a[tmp[i]],a[tmp[j]]));
        }
    }
    return d;
}
inline ll read(){
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){
        x=x*10;x=x+c-'0';c=getchar();
    }
    return x*f;
}
int main(){
    inf=1000000000000000ll;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        a[i].x=read();a[i].y=read();
    }
    sort(a+1,a+1+n,cmp);
    printf("%.4lf\n",sqrt(merge(1,n)));
    return 0; 
}

猜你喜欢

转载自blog.csdn.net/lvyanchang/article/details/80330275