1.题目描述:
设S是平面上n个点的集合,在这一节中,我们考虑在S中找到一个点对p和q的问题,使其相互距离最短。换句话说,希望在S中找到具有这样性质的两点p1 = (x1,y1)和p2 = (x2,y2),使它们间的距离在所有S中点对间为最小
2.解题思路
一共分为三种情况
情况1:点数小于等于二时:直接计算,求该两点之间的距离。
情况2:集合中有三个点:两两比较,求三个点中的最近的两个点距离。
情况3:点数大于三时:首先划分集合S为SL和SR,使得SL中的每一个点位于SR中每一个点的左边,并且SL和SR中点数相同。分别在SL和SR中解决最近点对问题,得到DL和DR,分别表示SL和SR中的最近点对的距离。令d=min(DL,DR)。如果S中的最近点对(P1,P2)。P1、P2两点一个在SL和一个在SR中,那么P1和P2一定在以L为中心的间隙内,以L-d和L+d为界。如图1.21
图 1.21
对于情况3, 如果在SL中的点P和在SR中的点Q成为最近点对,那么P和Q的距离必定小于d。因此对间隙中的每一个点,在合并步骤中,只需要检验yp+d和yp-d内的点即可。
步骤1:根据点的y值和x值对S中的点排序。
步骤2:找出中线L将S划分为SL和SR
步骤3:将步骤2递归的应用解决SL和SR的最近点对问题,并令d=min(dL,dR)。
步骤4:将L-d~L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的接下来的7个点,计算距离为d’。如果d’小于d,令d=d’,最后的d值就是答案。
对于合并结果的解释:对于划分后的图如图2.2.2
图 2.2.2
解释:
- 设d = min{dll, drr},如果最近点对由Sl中的某个点pl与Sr中的某个点pr组成,则pl和pr一定在划分线L的距离d内。这样,如果令S’l和S’r分别表示为在线L距离内的Sl和Sr 中的点,则pl一定在S’l中, pr一定在S’r中。
- 假设d’≤ d ,则存在两点pl∈S’l和pr∈S’r ,有d(pl, pr) = d’,从而pl和pr之间的垂直距离不超过d
- 因为pl,pr这两点都在以垂直线L为中心的d×2d矩形区内或其边界上
- 设T是两个垂直带内的点的集合
- 如果在d×2d矩形区内,任意两点间的距离一定不超过d,则这个矩形最多能容纳8个点,其中至多4个点属于Sl, 4个点属于Sr。
- T中的每个点最多需要和T中按照y轴排序后邻接的7个点进行比较。
3.代码如下:
1 // Closest-Pair.cpp: 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include<ctime>
6 #include<iostream>
7 #include<cmath>
8 #include<algorithm>
9
10 using namespace std;
11 //定义横纵坐标对大范围
12 #define RANGE 100.0
13 //定义无限大数字
14 #define INFINATE_DISTANCE 65535
15 //point 点对结构体
16 typedef struct Point {
17 double x;
18 double y;
19 }Point;
20 //随机生成一定数量坐标
21 void SetPoints(Point *points, int length) {
22 srand(unsigned(time(NULL)));
23 for (int i = 0; i < length; i++) {
24 points[i].x = (rand() % (int)(RANGE * 200)) / RANGE - RANGE;
25 points[i].y = (rand() % (int)(RANGE * 200)) / RANGE - RANGE;
26 }
27 }
28 //求出两点之间距离->Distance
29 double Distance(Point point1, Point point2) {
30 return sqrt((point1.x - point2.x)*(point1.x - point2.x) + (point1.y - point2.y)*(point1.y - point2.y));
31
32 }
33 //自定义排序,按照x坐标排序
34 bool CompX(Point a, Point b) {
35 return a.x < b.x;
36 }
37 //自定义排序,按照Y坐标排序
38 bool CompY(Point a, Point b) {
39 return a.y < b.y;
40 }
41 //利用分治思想进行查找最近点对->ClosestPair()
42 double ClosestPair(Point points[], int length, Point &a, Point &b) {
43 double distance;
44 double d1, d2;//左右两边子集各自的最近距离
45 Point a1, b1, a2, b2;//分割后生成的左右两边子集的最近点对
46 int i, j, k,m=0;
47 //先判断length的个数
48 if (length <2) {
49 distance = INFINATE_DISTANCE;
50 }
51 else if (length == 2) {
52 a = points[0];
53 b = points[1];
54 distance = Distance(a, b);
55 }
56 else {
57
58
59 //如果长度大于3,则生成两个子集
60
61 //先将points按照x坐标进行升序排序
62 sort(points, points + length, CompX);
63 //将升序后的左半边子集赋值给ptsl,右半边赋值给ptsr
64 Point *ptsl=new Point[length];
65 Point *ptsr = new Point[length];
66 for (i = 0; i < (length) / 2; i++) {
67 ptsl[i] = points[i];
68 }
69 for (j = 0, k = length / 2; k < length; k++) {
70 ptsr[j++] = points[k];
71 }
72 //对左半边子集进行递归操作,返回d1
73 d1 = ClosestPair(ptsl, length / 2, a1, b1);
74 d2 = ClosestPair(ptsr, length - (length / 2), a2, b2);
75 //对右半边子集进行递归操作,返回d2
76
77 //比较d1,d2,求出两者之间最小的距离
78 //distance = d1 < d2 ? d1 : d2;
79 if (d1 < d2) {
80 distance = d1; a = a1; b = b1;
81 }
82 else {
83 distance = d2; a = a2; b = b2;
84 }
85 //取得距离中线2δ距离的共K个点
86 double mid = points[(length - 1) / 2].x;//中线
87 Point *pts = new Point[length];
88 for (i = 0, k = 0; i < length; i++) {
89 if (abs(points[i].x - mid) <= distance) {
90 pts[k++] = points[i];
91 }
92 }
93 //将这K个点按照y坐标排序
94 sort(pts, pts + k, CompY);
95 //将左边的点与右半边的排好序的6个点进行距离比较
96 for (i = 0; i < k; i++) {
97 //只判断左侧部分的点
98 if (pts[i].x - mid > 0)
99 continue;
100 m = 0;
101 //只对按照Y排序后的邻接6个点进行比较
102 for (j = i + 1; j < i + 6 + m&&j<k; j++) {
103 if (pts[j].x - mid < 0) {
104 //只判断中线右边的点
105 m++;
106 continue;
107 }
108 if (Distance(pts[i], pts[j]) < distance) {
109 distance = Distance(pts[i], pts[j]);
110 a = pts[i];
111 b = pts[j];
112 }
113 }
114
115 }
116 }
117 return distance;
118 }
119
120
121 int main()
122 {
123 int num;
124 Point a, b;
125 Point *points;
126 double distance;
127 cout << "请输入点对的个数:";
128 cin >> num;
129 if (num < 2) {
130 cout << "请输入数目超过2的个数" << endl;
131 }
132 else {
133
134 points = new Point[num];
135 SetPoints(points, num);
136 cout << "随机生成的二维点对如下:" << endl;
137 for (int i = 0; i < num; i++) {
138 cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
139
140 }
141 distance = ClosestPair(points, num, a, b);
142 cout << endl <<endl << "按照横坐标排序后的点对为:" << endl;
143 for (int i = 0; i < num; i++) {
144 cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
145 }
146 cout << "最近距离的点对为:" << endl;
147 cout << "(" << a.x << "," << a.y << ")" <<" "<< "(" << b.x << "," << b.y << ")" << endl;
148 cout << "最近点对之间的距离为:" << endl;
149 cout << distance << endl;
150 }
151 return 0;
152 }
4.结果截图