计算几何 平面最近点对 nlogn分治算法 求平面中距离最近的两点

平面最近点对,即平面中距离最近的两点


分治算法:

int SOLVE(int left,int right)//求解点集中区间[left,right]中的最近点对

{

double ans; //answer

0)    调用前的预处理:对所有点排序,以x为第一关键词y为第二关键字 , 从小到大;

1)    将所有点按x坐标分成左右两部分;

/*      分析当前集合[left,right]中的最近点对,有两种可能:

1. 当前集合中的最近点对,点对的两点同属于集合[left,mid]同属于集合[mid,right]

则ans = min(集合1中所有点的最近距离, 集合2中所有点的最近距离)

2. 当前集合最近点对中的两点分属于不同集合:[left,mid][mid,right]

则需要对两个集合进行合并,找出是否存在p∈[left,mid],q∈[mid,right],使得distance(p,q)小于当前ans(即步骤1中求得的ans);

*/

2)    Mid = (left+right)/2;

ans = min( SOLVE(left,mid), SOLVE(mid,right) );

即:递归求解左右两部分中的最近距离,并取最小值;

//此步骤实现上文分析中的第一种情况

/*

再次进行分析

我们将集合[left,right]用x = mid这条直线分割成两部分

则如果画出直线l1:x=mid-ans 和 l2:x=mid+ans,显然如果有p∈[left,mid], q∈[mid,right]且distance(p,q) < ans则p,q一定在直线l1和直线l2之间,否则distance(p,q)必定大于ans。

于是扫描出在l1和l2之间的点

*/

3)    建立缓存数组temp[];

for i = left TO right

{

如果 abs(Point[i].x - Point[mid].x) <= ans

则向temp中加入点Point[i];

}

/*

对于temp中的点,枚举求所有点中距离最近两点的距离,然后与ans比较即可。

枚举的时候不必两两枚举。观察下图中的点p

不难发现,若有q∈[mid,mid+ans]使得distance(p,q) <
ans,则q点的位置一定在图中画出的一个2ans×ansd的矩形中。可以证明点集[mid,mid+ans]中的、矩形外的点与p点的距离一定大于
ans。
           于是我们可以对temp以y为唯一关键字从小到大排序,进行枚举, 更新ans,然后在枚举时判断:一旦枚举到的点与p点y值之差大于ans,停止枚举。最后就能得到该区间的最近点对。

*/

4)    sort(temp);

for i = 0 TO k-1

{

for j = i+1 TO k-1

如果 temp[j].y - temp[i].y >= ans  break;

ans = min( ans, distance(temp[i], temp[j]) );

}

5)    return ans;

}

算法的时间复杂度

        由鸽巢原理,代码中第四步的枚举实际上最多只会枚举6个点,效率极高(一种蒟蒻的证明请看下方的评论)

本算法时间复杂度为O(n log n)

代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MIN( x , y ) ( (x) < (y) ? (x):(y) )

struct _Point
{
    long long x;
    long long y;
}Points[100000] , Tmp[1000];

int cmpxy ( const void *pa , const void *pb )
{
    struct _Point *a = (struct _Point *)pa;
    struct _Point *b = (struct _Point *)pb;
    if ( a->x == b->x )
        return a->y > b->y ? 1:-1;
    else
        return a->x > b->x ? 1:-1;
}

int cmpy ( const void *pa , const void *pb )
{
    struct _Point *a = (struct _Point *)pa;
    struct _Point *b = (struct _Point *)pb;

    return a->y > b->y ? 1:-1;
}

double dis ( struct _Point a , struct _Point b )
{
    return sqrt ( (double) ( (a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) ) );
}

/*分治法求计算几何中平面点最近两点距离*/
double min_length ( struct _Point *p , long left , long right )
{
    double min;
    double d1,d2;
    long mid;
    long i , j ,k;

    if ( left == right )
        return -1;

    if ( left + 1 == right )
        return dis ( p[ left ] , p[ right ] );

    mid = ( left + right ) >> 1;
    d1 = min_length ( p , left , mid );
    d2 = min_length ( p , mid , right );
    min = MIN( d1 , d2 );

    for ( k = 0 , i = left ; i <= right ; i++ ) {

        if ( fabs ( p[i].x - p[mid].x ) <= min )
            Tmp[k++] = p[i];
    }

    qsort ( Tmp , k , sizeof ( Tmp[0] ) , cmpy );
    for ( i = 0 ; i < k - 1 ; i++ ) {

        for ( j = i + 1 ; j < k ; j++ ) {

            if ( fabs( Tmp[i].y - Tmp[j].y ) >= min )
                break;

            min = MIN ( min , dis ( Tmp[i] , Tmp[j] ) );
        }
    }

    return min;
}

int main ( int argc , char *argv[] )
{
    long n;
    long i;

    scanf("%ld" , &n );

    for ( i = 0 ; i < n ; i++ )
        scanf ("%ld%ld" , &Points[i].x , &Points[i].y );

    qsort ( Points , n , sizeof ( Points[0] ) , cmpxy );
    printf ("%.3f" , min_length ( Points , 0 , n - 1 ) );
    return 0;
}

部分转载:http://blog.csdn.net/lytning/article/details/25370169

时间: 2024-08-05 00:18:40

计算几何 平面最近点对 nlogn分治算法 求平面中距离最近的两点的相关文章

平面最近点对问题(分治)

平面最近点对问题是指:在给出的同一个平面内的所有点的坐标,然后找出这些点中最近的两个点的距离. 方法1:穷举 1)算法描述:已知集合S中有n个点,一共可以组成n(n-1)/2对点对,蛮力法就是对这n(n-1)/2对点对逐对进行距离计算,通过循环求得点集中的最近点对2)算法时间复杂度:算法一共要执行 n(n-1)/2次循环,因此算法复杂度为O(n2) 代码实现: 利用两个for循环可实现所有点的配对,每次配对算出距离然后更新最短距离. for (i=0 ; i < n ;i ++){ for(j=

Luogu P1429 平面最近点对 【分治】By cellur925

题目传送门 题目大意:给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的.$n$<=100000. $Algorithm$ 最朴素的$n^2$枚举肯定是不行了,我们在这个数量级只能考虑$nlogn$做法.那么与这个数量级比较相关的也就是分治了. 把整个平面分为两个部分,分别求出两个部分点对间最小的距离,之后再处理跨区域的情况. ? 分治法求解步骤: O(NlogN)  by hzwer1 将点集 S 分为两个?集 SL 和 SR 分别求解2 记 δ 为

求平面最近点对(分治与递归,注:最远点对用凸包求)

Quoit Design Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 36793    Accepted Submission(s): 9552 Problem Description Have you ever played quoit in a playground? Quoit is a game in which flat

平面最近点对(分治)

题目描述 给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的 输入输出格式 输入格式: 第一行:n:2≤n≤200000 接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开. 输出格式: 仅一行,一个实数,表示最短距离,精确到小数点后面4位. 1 //分治基础题 2 #include<cmath> 3 #include<vector> 4 #include<cstdio> 5 #include&l

Graham算法求平面散点集的凸包

本文参考自<<算法导论>>章节33.3 寻找凸包 完整VS2010工程见:http://download.csdn.net/detail/tangxin19930330/9406625 算法主要利用向量的叉积判断点和线段的位置关系,详见 向量叉积,然后从左下角点按逆时针方向寻找最边缘的线段,利用的原理就是从凸包上任意一点逆时针出发,每到一个节点,一定会向左拐.算法复杂度为O(nlg(n)) 算法主要实现如下: 1 // 输入:点数组arrInPt,个数nInPtCount,包含所有

POJ 3714 Raid 平面最近点对

题目大意:给出两个集合的点,问这两个集合之间最近的点对的距离是多少. 思路:先要知道平面最近点对的分治算法,剩下的就简单了,只需要在更新答案的时候判断一下两个点是否属于两个集合就可以了. 分治算法总是十分神奇的. 对于平面最近点对,首先按照x坐标排序,然后递归进行分治,每次分治时,先获得分治得到的结果,然后按照这个结果来计算本区间.由于我们只需要计算答案小于这个结果的点对就行了,其中(l,mid)和(mid + 1,r)我们已经得到答案,只需要统计一个点在(l,mid),另一个点在(mid +

平面最近点对(分治nlogn)

平面最近点对,是指给出平面上的n个点,寻找点对间的最小距离 首先可以对按照x为第一关键字排序,然后每次按照x进行分治,左边求出一个最短距离d1,右边也求出一个最短距离d2,那么取d=min(d1, d2) 然后只需考虑横跨左右两侧的点,不妨枚举左侧的点pi 那么很显然的是如果pi距离中间的点超过了d,便可以直接舍去,只需考虑距离中间点小于d的点 这样一来就可以对每个pi画一个边长为2d的正方形,易证,矩形内最多存在8个点. 那么关键问题就是要快速找这8个点 朴素做法是对分治后的点进行快排,这样复

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛11 B  假的字符串题意:给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们. tags:好题 对于一个字符串, 1]如有其它字符串是它的前缀,那肯定不可能.这个直接用字典树处理就可以. 2]但如果以这个字符串为最小,怎么判定其它字符串不会矛盾呢? 其实矛盾的情况详细一点说是: 比如要以  abcd 为最小, 但又有另一个字符串 aba ,这就矛盾了. 对这种情况,在跑字典树的时候,我们对有相同父亲结点的多个儿

Luogu 1429 平面最近点对 | 平面分治

Luogu 1429 平面最近点对 题目描述 给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的 输入输出格式 输入格式: 第一行:n:2≤n≤200000 接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开. 输出格式: 仅一行,一个实数,表示最短距离,精确到小数点后面4位. 这是一道平面上的分治. 这是一个平面,我们把它分成两半,使x坐标位于最中间的两个点分到左右两侧: 对于同在左侧或同在右侧的点对,我们可以递归处理: