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

平面最近点对问题是指:在给出的同一个平面内的所有点的坐标,然后找出这些点中最近的两个点的距离.

方法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= i+1 ; j<n ;j ++){
              点i与点j的配对
       }
}

  

方法2:分治

1) 把它分成两个或多个更小的问题;

2) 分别解决每个小问题;

3) 把各小问题的解答组合起来,即可得到原问题的解答。小问题通常与原问题相似,可以递归地使用分而治之策略来解决。

在这里介绍一种时间复杂度为O(nlognlogn)的算法。其实,这里用到了分治的思想。将所给平面上n个点的集合S分成两个子集S1和S2,每个子集中约有n/2个点。然后在每个子集中递归地求最接近的点对。在这里,一个关键的问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对。如果这两个点分别在S1和S2中,问题就变得复杂了。

为了使问题变得简单,首先考虑一维的情形。此时,S中的n个点退化为x轴上的n个实数x1,x2,...,xn。最接近点对即为这n个实数中相差最小的两个实数。显然可以先将点排好序,然后线性扫描就可以了。但我们为了便于推广到二维的情形,尝试用分治法解决这个问题。

假设我们用m点将S分为S1和S2两个集合,这样一来,对于所有的p(S1中的点)和q(S2中的点),有p<q。

递归地在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设

                  d = min{ |p1-p2| , |q1-q2| }                   

由此易知,S中最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{q3,p3},如下图所示。



    如果最接近点对是{q3,p3},即|p3-q3|<d,则p3和q3两者与m的距离都不超过d,且在区间(m-d,d]和(d,m+d]各有且仅有一个点。这样,就可以在线性时间内实现合并。

    此时,一维情形下的最近点对时间复杂度为O(nlogn)。

在二维的情况下:

我们仿照一维的情况先把所有点按照x(横坐标)从左到右升序排列.

以X横坐标中间的点作为分界线.将平面的点分成左边和右边,以上图为例,分为左边5个右边5个.

然后找到左边的点中最近点对的距离d1,和右边最近点对的距离d2。

令d=min{d1,d2}.那么d是当前整个平面的最近点对的距离吗?

答案不是!!

当前的d1,d2都是两边各自的点的最近距离,但是没有考虑到两边的点相互配对的情况,即是点对一个在左边区域,一个在右边区域.

如果我们这个时候把两边相互配对的所有情况全部罗列出来那么时间复杂度就变为第一种方法的O(n2).

这个时候我们便可以用上面找到的d来限制配对.即是明显超过d的两点不需要配对.如下:

那么在范围内的只有三个点,也就是说只有这个三个点相互配对的距离才可能比d小.那么再按照方法一一样在这些点中找到最短距离再跟d去比较.小的就是当前整个平面中所考虑的所有点中最近点对的距离。

然而在以上问题处理上,有一个问题尚未解决就是如何找到两边区域中的最近点对的距离d1,d2 ?

我们可以发现在处理上面这个问题的时候,和最开始处理所有平面的点最近点距离的问题类似,只是点的数目减半了.

那么我们可以递归以上问题.直到划分的区域中只有一个或者两个点.这样,该区域的最近点对距离就是无穷大或者该两点的距离。这也是递归终止的条件。

代码实现:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 using namespace std;
 7 const double inf = 1e20;
 8 const int maxn = 100005;
 9
10 struct Point{
11     double x, y;
12 }point[maxn];
13
14 int n, mpt[maxn];
15
16
17 //以x为基准排序
18 bool cmpxy(const Point& a, const Point& b){
19     if (a.x != b.x)
20         return a.x < b.x;
21     return a.y < b.y;
22 }
23
24 bool cmpy(const int& a, const int& b){
25     return point[a].y < point[b].y;
26 }
27
28 double min(double a, double b){
29     return a < b ? a : b;
30 }
31
32 double dis(int i, int j){
33     return sqrt((point[i].x - point[j].x)*(point[i].x - point[j].x) + (point[i].y - point[j].y)*(point[i].y - point[j].y));
34 }
35
36 double Closest_Pair(int left, int right){
37     double d = inf;
38     if (left == right)
39         return d;
40     if (left + 1 == right)
41         return dis(left, right);
42     int mid = (left + right) >> 1;
43     double d1 = Closest_Pair(left, mid);
44     double d2 = Closest_Pair(mid + 1, right);
45     d = min(d1, d2);
46     int i, j, k = 0;
47     //分离出宽度为d的区间
48     for (i = left; i <= right; i++){
49         if (fabs(point[mid].x - point[i].x) <= d)
50             mpt[k++] = i;
51     }
52     sort(mpt, mpt + k, cmpy);
53     //线性扫描
54     for (i = 0; i < k; i++){
55         for (j = i + 1; j < k && point[mpt[j]].y - point[mpt[i]].y<d; j++){
56             double d3 = dis(mpt[i], mpt[j]);
57             if (d > d3)    d = d3;
58         }
59     }
60     return d;
61 }
62
63 int main(){
64     while (~scanf("%d", &n) && n){
65         for (int i = 0; i < n; i++)
66             scanf("%lf %lf", &point[i].x, &point[i].y);
67         sort(point, point + n, cmpxy);
68         printf("%.2lf\n", Closest_Pair(0, n - 1) / 2);
69     }
70     return 0;
71 }
时间: 2024-12-06 06:34:58

平面最近点对问题(分治)的相关文章

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

平面最近点对,即平面中距离最近的两点 分治算法: int SOLVE(int left,int right)//求解点集中区间[left,right]中的最近点对 { double ans; //answer 0)    调用前的预处理:对所有点排序,以x为第一关键词y为第二关键字 , 从小到大; 1)    将所有点按x坐标分成左右两部分; /*      分析当前集合[left,right]中的最近点对,有两种可能: 1. 当前集合中的最近点对,点对的两点同属于集合[left,mid]或同属

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

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个点 朴素做法是对分治后的点进行快排,这样复

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

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

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

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

分治 -- 平面最近点对

平面最近点对 : 分析各种情况 : 首先将所有点对按照 x 作为第一关键字进行排序,然后从中间进行劈开,进行递归分治 最后答案就是 res = min(l -- mid,mid + 1 -- r); 从上图可以得知 : 要求在这个平面内所有点中的最近点对,会有三种情况: 1.两个点都在左侧 2.两个点都在右侧 3.一个点在左侧,一个点在右侧 分别向两侧递归进行分治 : 寻找最近点对 : 1.找到左侧的最近点对 2.找到右侧的最近点对 进行比较我们就会得到一个左侧和右侧之中的最近的一对 这时我们会