最近点对问题,大概意思平面有n个点,求距离最近的两个点对的距离(可用平面分治解决绝大部分情况)

首先如果一个一个比较,那就是n的二次方复杂度,那很多情况都会超时

我们考虑使用分治法,大概思路就是将这个平面分成两半(横的分竖的分都行,这里讲的是根据横坐标竖着分)
注意我们这个不是按照(最前的x坐标 + 最后的x坐标)/ 2这么分,因为这样可能会递归深度退化,我们应该按照最中间的那个点的x坐标来分,这样保证每次递归,点的数量都减半。

分治思路就是把点按x坐标分成两半,有两种情况:
(1)若两点同在左半边或右半边时的最小距离
(2)若两点分别在左半边和右半边时的最小距离

对于(1)我们递归的结果返回就能得到,我们重点考虑(2)
对于我们从(1)中得到的最小距离,我们记为 d ,我们只需考虑两点距离小于 d 的情况(且这两点在左右两边),所以距离中间点的 x坐标差 大于等于d的点我们可以不考虑,并且我们不再需要考虑同边

对于处理(2)大概讲三条思路(只有deal函数中处理(2)方法是不同的,为了节省篇幅,后面两种只写了修改后的deal函数)

第一种:对于每个离中间点的 x坐标差 小于 d 的左边点,我们遍历每个离中间点的 x坐标差 小于 d 的 右边点,取距离最小值返回即可


【相当于我们对于 左边虚线内的所有点 遍历一次 右边虚线内所有点,由于 d 的值会不断减小,所以不用担心遍历太多次。】

缺点:我们遍历了一些 y坐标差 大于等于 d 的不可能为最小距离的多余情况

代码如下(这种在P1429中所耗 687ms / 3.62MB / 1.52KB )

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#define INF (double)1e18
using namespace std;
pair<double, double> data[200005];
bool cmp(pair<double, double> p1, pair<double, double> p2)
{if(p1.second == p2.second){return p1.first < p2.second;}return p2.second < p2.second;
}
double dist(pair<double, double> p1, pair<double, double> p2)
{double num = (p1.first - p2.first) * (p1.first - p2.first) + (p1.second - p2.second) * (p1.second - p2.second);return sqrt(num);
}
double deal(int q, int t)
{if(t - q == 1){return INF;}double a = deal(q, (q + t) / 2);double b = deal((q + t) / 2, t);double x_mid = data[(q+t)/2].first;double d = min(a, b);int i = upper_bound(data + q, data + (q + t) / 2, make_pair(x_mid - d, INF)) - data;int jj = upper_bound(data + (q + t) / 2, data + t, make_pair(x_mid + d, INF)) - data;for(; i < (q + t) / 2; i++){for(int j = (q + t) / 2; j < jj; j++){if(data[i].first - data[j].first >= d){break;}else{d = min(d, dist(data[i], data[j]));}}}return d;
}
int main()
{int N;scanf("%d", &N);for(int i = 0; i < N; i++){scanf("%lf %lf", &data[i].first, &data[i].second);}sort(data, data + N);double jg = deal(0, N);printf("%.4lf\n", jg);
}

第二种:我们在递归处理的同时,按 y坐标 进行归并排序

这样的话我们无法判断直接判断哪个点的 x坐标 离中间点的 横坐标 距离小于 d ,所以我们遍历所有点,当点的 x坐标 在距离中间点的 横坐标 距离小于 d 时进行处理,处理方法为,对一定范围的点,进行遍历比较,如下图

【相当于我们对于所有点遍历一次,如果 x坐标 在范围内,我们便对阴影区域进行遍历,由于有 d 的限制,所以除去自身,该矩形阴影内最多还有 5 个点。】

缺点:我们遍历了所有点,有 x坐标 距离中间点横坐标超过 d 的多余情况

代码如下(这种在P1429中所耗 1.19s / 8.03MB / 1.45KB )

double deal(int q, int t)
{if(t - q == 1){return INF;}double x_mid = data[(q+t)/2].first;double a = deal(q, (q + t) / 2);double b = deal((q + t) / 2, t);double d = min(a, b);inplace_merge(data + q, data + (q + t) / 2, data + t, cmp);vector<int> dian;for(int i = q; i < t; i++){if(data[i].first <= x_mid - d || data[i].first >= x_mid + d){continue;}for(int j = dian.size() - 1; j >= 0; j--){if(data[i].second - data[dian[j]].second >= d){break;}d = min(d, dist(data[i], data[dian[j]]));}dian.push_back(i);}return d;
}

第三种:因为同边的点的最小值已经求出为 d ,所以不用 同边 的之间再次比较,所以我们可以分开两个vector容器分别存左边和右边的 x坐标 在范围内的点

但是这种会产生一种问题,如果在 中间点的横坐标 的点,我们不知道它属于左边还是右边,所以我们左右两边都得存放,且要和左右两边都比较一次
这种优化很不明显,甚至当在中间点的横坐标上的点多了的时候会退化,且原本范围内也就只用匹配最多五次,改了也就省了一两次匹配,还增加了大量代码量

缺点多着

代码如下( 1.58s / 8.07MB / 2.25KB )

double deal(int q, int t)
{if(t - q == 1){return INF;}double x_mid = data[(q+t)/2].first;double a = deal(q, (q + t) / 2);double b = deal((q + t) / 2, t);double d = min(a, b);inplace_merge(data + q, data + (q + t) / 2, data + t, cmp);vector<int> right;vector<int> left;for(int i = q; i < t; i++){if(data[i].first <= x_mid - d || data[i].first >= x_mid + d){continue;}if(data[i].first <= x_mid){left.push_back(i);for(int j = right.size() - 1; j >= 0; j--){if(i == left[j]){continue;}if(data[i].second - data[right[j]].second >= d){break;}else{d = min(d, dist(data[i], data[right[j]]));}}}if(data[i].first >= x_mid){right.push_back(i);for(int j = left.size() - 1; j >= 0; j--){if(i == left[j]){continue;}if(data[i].second - data[left[j]].second >= d){break;}else{d = min(d, dist(data[i], data[left[j]]));}}}}return d;
}

平面分治详解 超级详细(附带例题 最近点对问题(给了题目))(UVA10245,P1257,P1429)相关推荐

  1. 树上分治详解 超级详细(附带例题 poj1741(给了题目))

    例题大概意思就是有一颗有 n 个顶点的树,其中连接顶点 a_i 和 b_i 的边 i 的长度为 l ,然后统计最短距离不超过 k 的顶点的对数 (虽然篇幅比较长,但是看完会有收获的) 树上的分治,与其 ...

  2. 最小路径覆盖详解 超级详细(附带例题 Stock Charts(给了题目))

    最小路径覆盖定义:在图G中找出一些路径,每条路径从起点走到终点,使所有点均被覆盖,且只被覆盖一次,选出的这些路径组成路径覆盖.如果找出最少的路径成为一个路径覆盖,则称为最小路径覆盖. 对于不包含圈的有 ...

  3. Redis五种数据类型应用场景详解(超级详细版)

    目录 NoSQL:一类新出现的数据库(not only sql) 特征 NoSQL和SQL数据库的比较: 电商场景解决方案 Redis简介 Redis特性 Redis 优势 Redis应用场景 官方文 ...

  4. 强连通分量分解详解 超级详细

    (写的有点小多,慢慢看,会有收获的) (1) 首先我们得了解,什么是强连通? 如果在一个有向图顶点子集内,任取两个点 u 和 v ,都能找到一条路径从 u 到 v ,则称该子集为强连通 (2) 其次我 ...

  5. Spring AOP全面详解(超级详细)

    如果说IOC 是 Spring 的核心,那么面向切面编程AOP就是 Spring 另外一个最为重要的核心@mikechen AOP的定义 AOP (Aspect Orient Programming) ...

  6. Java基本注解详解(超级详细)

    原文链接: 今日头条 Java注解是一个很重要的知识点,用于对代码进行说明,可以对包.类.接口.字段.方法参数.局部变量等进行注解. 掌握好Java注解有利于学习框架底层实现.@mikechen Ja ...

  7. kickstart详解(超级详细)

    一.kickstart是什么   许多系统管理员宁愿使用自动化的安装方法来安装红帽企业 Linux.为了满足这种需要,红帽创建了kickstart安装方法.使用kickstart,系统管理员可以创建一 ...

  8. NLP:Transformer的架构详解之详细攻略(持续更新)

    NLP:Transformer的架构详解之详细攻略(持续更新) 目录 Transformer的架构详解 1. Encoder 1.1.Positional Encoding-数据预处理的部分 1.2. ...

  9. NLP:Transformer的简介(优缺点)、架构详解之详细攻略

    NLP:Transformer的简介(优缺点).架构详解之详细攻略 目录 Transformer的简介(优缺点).架构详解之详细攻略 1.Transformer的简介 (1).Transforme的四 ...

最新文章

  1. 课程作业1:字符型强制转化为整型
  2. 计算当前日期是一年中的第几周
  3. 微信机器人 java 源码_三步轻松打造微信聊天机器人(附源码)
  4. TianyaDL_4thread天涯帖子下载4线程版
  5. 吴恩达 神经网络和深度学习 第一课 第四周(代码和库)
  6. php 返回script,从PHP脚本返回JSON
  7. 你是否需要安全运营中心?
  8. DOS命令大全(经典收藏)【运行CMD后的命令】
  9. c语言冒泡排序法程序填空_五种数组的排序方法(C语言)Part1
  10. 海思Hi3519AV100 emmc flash方式 linux系统移植 hitool工具烧写
  11. 【安全思维导图】————4、Git思维导图
  12. 保存 Hexo 博客源码到 GitHub
  13. 3dsmax如何批量导入obj格式文件
  14. UE4入门序列06(Unreal源码编译及打包专用服务器)
  15. 电脑hosts文件位置
  16. 机器学习_深度学习毕设题目汇总——漫画
  17. Microsemi Libero SoC 教程2 (点亮LED闪烁)
  18. 华大单片机HC32L130/HC32L136从机IIC通信
  19. 【noip模拟赛5】细菌
  20. 关于单页应用(SPA)的经验之谈

热门文章

  1. JDK, JRE和JVM的区别与联系
  2. 实战centos6安装zabbix-2.4版(终极版)
  3. PC行业为何需要华为们被搅局?
  4. 记一次lnmp经历 nginx 多个php版本支持配置
  5. [分享]iOS开发-UI篇:CAlayer层的属性
  6. JavaScript:事件冒泡和事件委托
  7. 妙用终截者密码锁防***注入Explorer
  8. 适合数据科学小白的必备Python工具! ! !
  9. python中常见的数据类型_Python中常见的数据类型总结(四)
  10. drupal7 php版本,让Drupal7识别您的模块