《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——平面最近点对

2014.07.06 17:56

简介:

  给定二维平面上n个点,求出距离最近的两个点的距离。

描述:

  个人觉得这本书的第十章是最精华的,因为每种算法思想用一个典型问题来讲解。平面最近点对问题,自然是分治法的例子了。

  暴力的O(n^2)枚举法自然不用多说,两层循环能得出结果,但效率上只能承担至多几千个点的计算。

  分治法的基本思路是:如果我将n个点分为两批,那么距离最短的点要么同属一批(这样可以用子问题得到答案),要么各落在一批中(这样就必须合并子问题才能得到答案)。

  分解为子问题的过程自然很简单,选出一个分界点mid,然后将n个点分为两部分,对每一部分递归求最近距离点对。

  合并过程复杂一些,并且发生在递归调用结束之后。也就是:

    递归左半部分的子问题 -> 递归右半部分的子问题 -> 合并结果得出最终答案

  我们定义δ(delta)为左右两部分计算得出的最近距离的较小值。如果最终结果比δ还要小的话,那么它应该会出现在分界线mid处(否则的话,最终结果肯定早就在子问题中得到了,也就不可能比δ还小),而且是由离mid很近的一左一右两个点得到。因此以mid位置的点p[mid]为中心,δ为半径的正方形区域(不是圆形,因为不好计算)都是可能得到最小值的区域。

  那么,我们从下标mid开始向两边扫描,找出所有落在这个可疑区域的点,于是可以得到左右两个点集P和P。然后将两个集合中的点一一比对就能得到结果了。

  如果这样算法就完了,那么实际的程序运行甚至会比暴力算法还慢,因为数据如果一直是无序的,那就无法在该结束循环的时候提前结束循环。

  排序1:在算法的一开始,就应该选取一个坐标轴(比如X)进行排序,这样我们在“那么,我们从下标mid开始向两边扫描”这句话的时候,就有了break的条件了。如果X坐标是有序的, 那么当某个p[i]的X坐标与p[mid]相差超过了δ,就可以停止扫描了。

  排序2:“于是可以得到左右两个点集P和P”,尽管我们可以直接对两个候选集合中的点一一计算距离,得到最短距离。但这么做的话,效率仍然是暴力级别的。这些点全都落在了2δ为边长的正方形区域内,而且这个δ给人的印象很小很小,于是我们可能会认为P与P加起来点的数量很小,即使枚举也没关系的。在实际的递归调用里,一个看似小的浪费就会被放大到可观察的程度,然后你的代码就超时了。在排序1中X坐标已经有序了,那么可能变化的比较离谱的,只有Y坐标了。于是我们对候选集合中点按Y坐标进行排序。有序的数据,总能给break语句制造机会。这个排序让算法完整了。

  为了确认算法的效率,我把代码修改了一句话之后提交到了ZOJ的题目“Quoit Design”运行,结果的确能AC掉。

实现:

  1 // Divide-and-conquer solution for Closest Pair of Points problem.

  2 // This piece of code is also accepted on Zhejiang University Online Judge

  3 // Problem ID 2107.

  4 // http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1107

  5 #include <algorithm>

  6 #include <cmath>

  7 #include <cstdio>

  8 #include <vector>

  9 using namespace std;

 10 

 11 struct Point {

 12     double x;

 13     double y;

 14     Point(double _x = 0.0, double _y = 0.0): x(_x), y(_y) {}

 15 };

 16 

 17 bool comparatorX(const Point &a, const Point &b)

 18 {

 19     if (a.x == b.x) {

 20         return a.y < b.y;

 21     } else {

 22         return a.x < b.x;

 23     }

 24 }

 25 

 26 bool comparatorY(const Point &a, const Point &b)

 27 {

 28     if (a.y == b.y) {

 29         return a.x < b.x;

 30     } else {

 31         return a.y < b.y;

 32     }

 33 }

 34 

 35 double dist(const Point &a, const Point &b)

 36 {

 37     return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));

 38 }

 39 

 40 double closestPairOfPointsRecursive(const vector<Point> &points, int left, 

 41     int right)

 42 {

 43     if (right - left + 1 == 2) {

 44         return dist(points[left], points[left + 1]);

 45     } else if (right - left + 1 == 3) {

 46         return min(dist(points[left], points[left + 1]), 

 47             min(dist(points[left + 1], points[left + 2]), 

 48             dist(points[left], points[left + 2])));

 49     }

 50     

 51     int mid = left + (right - left) / 2;

 52     double delta = min(closestPairOfPointsRecursive(points, left, mid), 

 53         closestPairOfPointsRecursive(points, mid + 1, right));

 54     vector<Point> p;

 55 

 56     int i, j;

 57     i = mid;

 58     while (i >= left && points[i].x > points[mid].x - delta) {

 59         p.push_back(points[i]);

 60         --i;

 61     }

 62     

 63     i = mid + 1;

 64     while (i <= right && points[i].x < points[mid].x + delta) {

 65         p.push_back(points[i]);

 66         ++i;

 67     }

 68 

 69     int np;

 70     double result = delta;

 71 

 72     sort(p.begin(), p.end(), comparatorY);

 73     np = (int)p.size();

 74     for (i = 0; i < np; ++i) {

 75         for (j = i + 1; j < np; ++j) {

 76             if (p[j].y - p[i].y > result) {

 77                 break;

 78             }

 79             result = min(result, dist(p[i], p[j]));

 80         }

 81     }

 82     p.clear();

 83     

 84     return result;

 85 }

 86 

 87 double closestPairOfPoints(vector<Point> &points)

 88 {

 89     int n = points.size();

 90     sort(points.begin(), points.end(), comparatorX);

 91     return closestPairOfPointsRecursive(points, 0, n - 1);

 92 }

 93 

 94 int main()

 95 {

 96     vector<Point> points;

 97     int n;

 98     int i;

 99     

100     while (scanf("%d", &n) == 1 && n > 0) {

101         points.resize(n);

102         for (i = 0; i < n; ++i) {

103             scanf("%lf%lf", &points[i].x, &points[i].y);

104         }

105         // printf("%.2f\n", closestPairOfPoints(points) / 2.0);

106         printf("%.2f\n", closestPairOfPoints(points));

107     }

108     

109     return 0;

110 }

 

你可能感兴趣的:(数据结构与算法)