zoukankan      html  css  js  c++  java
  • 平面最近点对学习笔记

    平面上n个点构成了点集S,求集合S中的最近点对。

    求最近点对有两种方法:

    1.蛮力法(适用于n较小的情况下)

    2.分治法

    下面就来详细介绍一下这两种方法QWQ

    一、蛮力法

    1.算法描述

    已知集合S中有n个点,一共可以组成n(n-1)/2对点对,蛮力法就是对这n(n-1)/2对点对逐对进行距离计算,通过循环求得点集中的最近点对

    这种方法是真的名副其实超级暴力了,就是直接循环嵌套把n个点两两匹配找出最近点对

    2.核心代码

    double minn=123456;//minn用于存储最近点对之间的距离
    int s1,s2;//存储最近点对的两个点编号
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++){
            double dis=D(i,j);//D(i,j)表示点i和点j之间的距离
            if(dis<minn){//如果此两点间的距离小于当前最小距离就更新信息
                minn=dis;
                s1=i;
                s2=j;
            }
        }

    3.时间复杂度

    此算法一共要执行n(n-1)/2次,所以时间复杂度为O(n2)

    (不会算时间的孩纸伤不起啊QAQ)

    这种暴力的方法应该用得不是很多……?还是主要要掌握下面这种分治法呀^_^

    二、分治法

    1.算法描述

    分治法的思想就是将S进行拆分,分为2部分求最近点对。算法每次选择一条垂线L,将S拆分左右两部分为SL和SR,L一般取点集S中所有点的中间点的x坐标来划分,这样可以保证SL和SR中的点数目各为n/2

    这个方法可以用递归实现,对于当前状态下的垂线L,最近点对可能会有三种情况

    <1>最近点对出现在L的左侧

    <2>最近点对出现在L的右侧

    <3>最近点对跨过L,即最近点对一个点在L左侧,一个点在L右侧

    于是我们要先算出L左侧的最近点对和L右侧的最近点对,取这两个最近点对之间的距离较小值记为d,再从L向左右分别扩展长为d的距离,枚举在这一段范围内的点两两匹配,找到跨过L的最近点对再和d比较,这样就可以得到所有n个点中的最近点对了

    2.核心代码  (放一个模板的链接)

     1 #include<bits/stdc++.h>
     2 #define go(i,a,b) for(register int i=a;i<=b;i++)
     3 using namespace std;
     4 double INF=2<<20;
     5 const int N=200002;
     6 struct point{
     7     double x,y;
     8 }p[N];
     9 int n;
    10 int middle[N];
    11 double D(int a,int b){//用于计算两点之间的距离
    12     double X=p[a].x-p[b].x;
    13     double Y=p[a].y-p[b].y;
    14     return sqrt(X*X+Y*Y);
    15 }
    16 bool cmp1(const point &A,const point &B){//预处理
    17 //以横坐标为第一关键字,纵坐标为第二关键字排序
    18     if(A.x==B.x)
    19         return A.y<B.y;
    20     return A.x<B.x;
    21 }
    22 bool cmp2(const int &A,const int &B){
    23     return p[A].y<p[B].y;
    24 }
    25 double work(int l,int r){
    26     double d=INF;
    27     if(l>=r) return d;//如果只有一个点的话距离就是无穷大啦
    28     if(l+1==r){//两个点的话最短距离就是这两点之间的距离
    29         return D(l,r);
    30     }
    31     int mid=(l+r)/2;
    32     double dl=work(l,mid);//dl记录左半部分的最短距离
    33     double dr=work(mid+1,r);//dr记录右半部分的最短距离
    34     d=min(dl,dr);//取较小值
    35     int num=0;
    36     go(i,l,r)//找到中间扩展出的区域内的点
    37         if(abs(p[mid].x-p[i].x)<=d)//注意这里要取绝对值abs()函数
    38             middle[++num]=i;//记录
    39     sort(middle+1,middle+1+num,cmp2);//再次排序
    40 //这次排序以纵坐标为关键字,为后面的优化做准备
    41     go(i,1,num-1)//枚举记录好的点
    42         for(int j=i+1;j<=num&&p[middle[j]].y-p[middle[i]].y<d;j++){
    43 //因为是按纵坐标升序排列的,所以如果两个点的纵坐标之差已经大于当前最短
    44 //距离就可以跳过啦
    45             double dm=D(middle[i],middle[j]);//dm记录中间区域的最短距离
    46             d=min(d,dm);
    47         }
    48     return d;
    49 }
    50 int main(){
    51     scanf("%d",&n);
    52     for(int i=1;i<=n;i++)
    53     scanf("%lf%lf",&p[i].x,&p[i].y);
    54     sort(p+1,p+1+n,cmp1);
    55     printf("%.4lf
    ",work(1,n));
    56     return 0;
    57 }
    58 /*
    59    DTT小朋友说归并排序会比快排要优秀一些啊……不过我没有尝试诶,主要是
    60    快排也很优秀啦,而且快排打起来比归并要方便啊(对我就是懒)
    61 */
    在我锲而不舍地修改了n次之后终于AC的代码

    3.时间复杂度

    总的时间复杂度为O(3nlogn)

    (别问我怎么算的我是真的不会算TAT)

  • 相关阅读:
    Mysql数据操作指令
    Mysql列属性
    Mysql表的对应关系
    Mysql中的一些类型
    Mysql笔记
    (三) rest_framework 权限与限流源码梳理
    (二) rest_framework 认证源码流程与配置
    (一) rest_framework 视图入口
    django_celery_beat
    GRPC
  • 原文地址:https://www.cnblogs.com/THWZF/p/10359458.html
Copyright © 2011-2022 走看看