(学习5 分治思想)最短对的问题

最短对问题:

在一个拥有n个点的空间中,求其中欧几里得距离最小的点对。

解析

原理理解的参考视频:https://www.bilibili.com/video/BV1Y7411w71e?from=search&seid=14190590970009131408

1:点的数量较少的情况可以直接用蛮力算法计算出,比如 n=1或者2;

2:点的数量较多的情况,就需要利用分治的思想来实现,首先将每个点按照X轴升序排序,将点集合Q一分为二,称为Q1,Q2。然后分别求出Q1、Q2中最短点对d1与d2;但是我们不能断定求出的d=min(d1,d2)就是这个空间里最短点对,因为在Q1、Q2分割线两侧可能有两点之间的距离比d1和d2还小。所以我们设分隔线与x=m,找出Q点集中[m-d,m+d]之间的点,记该点集为Q3,然后将Q3中的点以y轴按升序排序,按照视频里的讲解,最多只用查找7个点,这是因为每个落在区域[m-d,m+d]的点,其对面的点一定落在一个边长为d的正方形边上,因为任何其他落在这个正方形以外的点的长度一定大于这个正方形边上的点,也不可能落在正方形里面,不然就违背了d最小的前提,所以更新d的操作的时间复杂度是比较低的,然后遍历找是否出现两点之间的距离少于d的,如果比d还短则更新d,最后输出的d就是该平面最小的距离。

代码

//
//  main.cpp
//  作业5
//
//  Created by yizhihenpidehou on 2020/3/24.
//  Copyright © 2020 yizhihenpidehou. All rights reserved.
//
/*问题:求二维空间中欧几里得距离最小的点对*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxen=50000;
struct Node{  //存储节点
    double x;
    double y;
};
bool cmpX(Node a,Node b){ //对X轴排序
    return a.x<b.x;
}
bool cmpY(Node a,Node b){ //对Y轴排序
    return a.y<b.y;
}
double dist(Node a,Node b){ //返回距离
    return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2));
}
Node arr[maxen];
double dirctSearch(int n,Node a[]){
    if(n==1) return 0;
    else return dist(a[1],arr[2]);
}
double EfficientClosestPair(int l,int r){
    int mid=(l+r)/2;
    int count=1;
    double d=0;
    if(l==r) return 999999;
    d=(min(EfficientClosestPair(l,mid),EfficientClosestPair(mid+1,r)));
    Node tmp[maxen];
    for(int i=mid;i>=l&&(arr[mid].x-arr[i].x<d);i--){  //筛选出左边间距小于d的
        tmp[count++]=arr[i];
    }
    for(int i=mid+1;i<=r&&(arr[i].x-arr[mid].x<d);i++){ //筛选出z右边间距d小于d的
        tmp[count++]=arr[i];
    }
    sort(tmp+1,tmp+count,cmpY); //将按d划分的区间中的点集合按照y轴排序
    for(int i=1;i<count;i++){
        for(int j=i+1;j<count&&(arr[j].y-arr[i].y<d);j++){
            d=min(d,dist(arr[i],arr[j]));//更新d
        }
    }
    return d;
}
int main(int argc, const char * argv[]) {
    int n;
    double minDist;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lf %lf",&arr[i].x,&arr[i].y);
    }
    if(n<=2){
        minDist=dirctSearch(n, arr); //若点的数量比较少,则直接暴力
    }
    else{   //采用分治思想,二分找出两个区间最小的点对距离,然后再找出按照最小点对划分的区间的中间区域查找是否有更短的点对
        sort(arr+1,arr+1+n,cmpX); //按照X轴排序
        minDist=EfficientClosestPair(1,n);
    }
 //   printf("%.2f\n",sqrt(10));
    printf("%.2f\n",minDist);
    return 0;
}

时间复杂度分析:最好的情况:o(logn),划分后区域Q1、Q2上的点比较稀疏

最坏的情况:o(n²),有可能所有点都在[m-d,m+d]上,这样第三次更新d就比较费时了

github地址:https://github.com/yizhihenpidehou/bananas/tree/master/%E7%AC%AC%E4%BA%94%E5%91%A8

原文地址:https://www.cnblogs.com/pipihoudewo/p/12562530.html

时间: 2024-11-15 00:42:26

(学习5 分治思想)最短对的问题的相关文章

分治思想在Trimino拼图中的Java实现

近期学习<算法设计与分析基础 第二版>,学习到了分治法,被课后习题吸引了,即Trimino拼图问题.想了好久,都没有想到如何去分而治之.然后就是Google到了相关的PPT.一看就明白了.自己就用代码实现了下.理解思想后,代码实现挺容易的. 这个谜题实际上可以做成一个小益智游戏. 分治思想的关键就在于构造相同的子问题.这是这个思想核心看似简单,实则需要对问题的洞察力.看到的PPT核心图如下: 算法的源代码如下: /**  * @author shuaiguangying  * 用分治法解决棋盘

算法学习 :分而治之思想

在刷LeetCode时遇到了一题可以用到分治思想的题目,刚好前段时间有看到过关于分治思想的讲解,但是不是很理解,这里再学习一次. 分而治之(divide and conquer,D&C)--一种著名的递归式问题解决方法. 使用分治解决问题的过程包括两个步骤: 1.找出基线条件,这种条件必须尽可能简单 2.不断将问题分解,或者说缩小规模,直到符合基线条件 编写涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素. 在实际做题时 只能选择相信这个分治思想是正确的,因为我在看到这种DC解法时

(转)深度学习前沿算法思想

深度学习前沿算法思想 本文转自: https://mp.weixin.qq.com/s?__biz=MzA5NDExMTAzNA==&mid=2649981646&idx=2&sn=8fe05eac5a5068efb65ca1602e5fd3a0&chksm=8854b69bbf233f8db5dbaa1ea8396d181c9a35e760ab3c1c4934b504f69fe92683972d78d327&mpshare=1&scene=2&sr

经典算法宝典——分治思想(四)(1)

分治法(Divide and Conquer)的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的几个相似问题,以便各个击破,分而治之. 说明: 分治策略的应用很广,具体表现形式各异,比如:折半查找.合并排序.快速排序.二叉树遍历(先遍历左子树再遍历右子树).二叉排序树的查找等算法. 一.分治算法框架 1.算法设计思想 分治法求解问题的过程是,将整个问题分解成若干个小问题后分而治之.如果分解得到的子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出方

分治思想--小测试(归并排序后续)

1 package cn.it; 2 3 import java.util.Arrays; 4 // 利用分治思想 实现 归并排序 5 public class Fz { 6 public static void main(String[] args) { 7 int a[]={1,2,3,4,5,6,8,7,3,4,9,0}; 8 int temp[] = new int[a.length]; 9 sortdiv(a, 0, a.length-1, temp); 10 System.out.p

排序算法大集锦_二路归并排序_2&3(分治思想)

第一段代码和合并排序差不多,用它来和第二段代码--二路归并排序作对比. 这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <limits.h> #include <malloc.h> void merge(int *m, int x, int y, int z) { int b1,b2,i,j,k; b1=y

归并排序,分治思想

merge函数将两列有序序列合成一列. merge_sort 函数使用分治思想,递归求解.将对一个序列排序转换成对左右两个序列排序,一直到序列长度为一时,递归开始回升.再将左右两个已经排好序的序列合并. // // main.cpp // merge_sort // // Created by Fangpin on 15/3/9. // Copyright (c) 2015年 FangPin. All rights reserved. // #include <iostream> #inclu

一次性弄懂到底什么叫做分治思想(含有大量经典例题,附带详细解析)

期末了,通过写博客的方式复习一下算法,把自己知道的全部写出来 分治:分而治之,把一个复杂的问题分解成很多规模较小的子问题,然后解决这些子问题,把解决的子问题合并起来,大问题就解决了 但是我们应该在什么时候用分治呢?这个问题也困扰了我很久,做题的时候就不知道用什么算法 能用分治法的基本特征: 1.问题缩小到一定规模容易解决 2.分解成的子问题是相同种类的子问题,即该问题具有最优子结构性质 3.分解而成的小问题在解决之后要可以合并 4.子问题是相互独立的,即子问题之间没有公共的子问题 第一条大多数问

算法--分治思想的运用

前言:上次算法课主要对分治思想进行了介绍,在这里进行以下总结和几个例子的应用. 一.分治算法 设计过程:(1)分解:将问题分解为子问题,子问题的形式与原问题是一样的,只是规模减小了. (2)求解:递归地求解出子问题. (3)合并:将子问题的解组合成原问题的解. 分治算法中最重要的就是递归求解子问题,求解递归式估计出算法的复杂度一般可以有三种方式: (1)代入法:猜测一个界,然后用数学归纳法证明这个界是正确的. (2)递归树法:将递归式转为一棵树,其结点表示不同层次的递归调用产生的代价. (3)主