热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

最近点对

最近点对设\(p_i(x_i,y_i)\),表示平面上的一个点。对于给定的点集\(S\),求最近点对。很容易想到\(O(n^2)\)的算法。计算每一对点的距离,然后取最小值。但今天

最近点对

设\(p_i = (x_i,y_i)\),表示平面上的一个点。

对于给定的点集\(S\),求最近点对。

很容易想到\(O(n^2)\)的算法。

计算每一对点的距离,然后取最小值。

但今天看分治的时候看到一种\(O(nlogn)\)的算法。

我们将点集合\(S\)按照\(x\)为第一关键字,\(y\)为第二关键字的大小升序排列,然后在拆分成集合大小相同的\(S_1\)和\(S_2\)。

根据分治的想法,最近点对只会有三种情况:



  • 两个点都在\(S_1\)集合中

  • 两个点都在\(S_2\)集合中

  • 一个在\(S_1\)一个在\(S_2\)。

前面两种可以递归的寻找,所以问题在于处理第三种情况。

假设情况1的最近点对距离为\(h_1\),情况2的最近点对距离为\(h_2\),\(h = min(h_1,h_2)\)。

用\(OIwiki\)上的这张图来观察一下:

nearest-points1

对于情况3的情况,所选的点对最好是位于中间中间轴左右\(h\)的区域内比较好,这个区域就是上图的\(B\)。

上图是在\(x\)方向考虑,现在考虑\(y\)方向。

对于每一个在\(B\)中的\(p_i\),只需要找到另外一个点\(p_j\),且纵坐标绝对值之差小于\(h\),考虑到重复的问题,我们让\(p_j\)的纵坐标大于\(p_i\)的纵坐标。有一个神奇的证明表示\(p_j\)的个数最大为7。

于是对于情况3我们有了一个算法:



  1. 构建\(B\)

  2. 将\(B\)按照纵坐标排序

  3. 对于每一个\(p_i\),找到最近距离更新答案

接下来就是使用分治法解决问题了,时间复杂度为\(O(nlogn)\)【虽然不太会证明这个问题】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define pii pair
const ll N = 1e6+50;
const ll inf = 0x3f3f3f3f;
struct Pt{
double x,y;
}pt[N];
ll n,f[N];
bool cmpxy(const Pt&a,const Pt& b){
return a.x == b.x?a.y}
bool cmpy(const ll& a,const ll& b){
return pt[a].y }
double dis(ll i,ll j){
ll dx = pt[i].x-pt[j].x,dy = pt[i].y-pt[j].y;
return sqrt(dx*dx+dy*dy);
}
double close_pair(ll l,ll r){
double h = 1e9+50;
if(l == r) return h;
if(l+1 == r) return dis(l,r);
ll mid = l+r>>1;
double h1 = close_pair(l,mid);
double h2 = close_pair(mid+1,r);
//cout< h = min(h1,h2);
ll k = 0;
for(ll i = l;i <= r;i++){
if(fabs(pt[mid].x-pt[i].x) <= h)f[++k] = i;
}
sort(f+1,f+k+1,cmpy);
for(ll i = 1;i <= k;i++){
for(ll j = i+1;j <= k;j++){
if(pt[f[j]].y-pt[f[i]].y >= h) break;
//printf("%lld %lld %.4lf\n",f[i],f[j],dis(f[i],f[j]));
h = min(h,dis(f[i],f[j]));
}
}
//printf("%lld %lld %.4lf\n",l,r,h);
return h;
}
int main() {
//ios::sync_with_stdio(false);
scanf("%lld",&n);
for(ll i = 1;i <= n;i++) scanf("%lf%lf",&pt[i].x,&pt[i].y);
sort(pt+1,pt+n+1,cmpxy);
// for(ll i = 1;i <= n;i++){
// printf("%lf %lf\n",pt[i].x,pt[i].y);
// }
printf("%.4lf\n",close_pair(1,n));
return 0;
}


推荐阅读
  • A题这题贼水,直接暴力就可以了。用个bool数组记录一下,如果某一天,当前剩下的最大的出现了的话,就输出一段。1#include<stdio.h>2intn;3boolvi ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • C语言常量与变量的深入理解及其影响
    本文深入讲解了C语言中常量与变量的概念及其深入实质,强调了对常量和变量的理解对于学习指针等后续内容的重要性。详细介绍了常量的分类和特点,以及变量的定义和分类。同时指出了常量和变量在程序中的作用及其对内存空间的影响,类似于const关键字的只读属性。此外,还提及了常量和变量在实际应用中可能出现的问题,如段错误和野指针。 ... [详细]
  • 本文介绍了一种图的存储和遍历方法——链式前向星法,该方法在存储带边权的图时时间效率比vector略高且节省空间。然而,链式前向星法存图的最大问题是对一个点的出边进行排序去重不容易,但在平行边无所谓的情况下选择这个方法是非常明智的。文章还提及了图中搜索树的父子关系一般不是很重要,同时给出了相应的代码示例。 ... [详细]
  • P2P学习(四)P2P编程实现
    一:协议解析(一)协议格式设计(二)字段说明Version(1Byte):版本信息,这里默认0即可Status(1Byte):协议的状态信息#definePROTO_LOGIN_R ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 如何在跨函数中使用内存?
    本文介绍了在跨函数中使用内存的方法,包括使用指针变量、动态分配内存和静态分配内存的区别。通过示例代码说明了如何正确地在不同函数中使用内存,并提醒程序员在使用动态分配内存时要手动释放内存,以防止内存泄漏。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 实现一个通讯录系统,可添加、删除、修改、查找、显示、清空、排序通讯录信息
    本文介绍了如何实现一个通讯录系统,该系统可以实现添加、删除、修改、查找、显示、清空、排序通讯录信息的功能。通过定义结构体LINK和PEOPLE来存储通讯录信息,使用相关函数来实现各项功能。详细介绍了每个功能的实现方法。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
author-avatar
縌风而行2010
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有