最简单的思想是直接进行遍历,也就是n*(n-1)的复杂度;
如果采用分治的思想会简单很多;
看了好几篇Blog,发现都不怎么讲人话,其实本质上是通过一维分治推出来的;
对于一维情况下:
对于一维数轴下找最近两点,可以按照坐标点进行分治;
直接递归二分区域,使得细分为左右各有一个点或者两个点的区域;
但是有可能发生以上的情况:
左半边区域S1最小距离位p1~p2的距离;
右半边区域S2最小距离位q1~q2的距离;
但是对于临近分界点会有p3~q3最小,也就是二分之后的区间合并区域;
所以可以看到,对于递归分治求S1和S2之后,如果需要合并,要在求三者区域的最小值;
对于p3~q3,可以寻找分界点即可,即使点mid~点mid+1,因为函数递归以点的个数为单位,事先进行排序;
总的调用情况位charge(left,mid,mid+1,right);
对于二维情况下:
对于二维情况下,发生了一些变化。
对于细分计算距离,还是利用二分,使得左右点为一个或者两个,之后进行层层整体合并;
仍然比较的使三个距离:
1.左半边区域S1最小距离位p1~p2的距离;
2.右半边区域S2最小距离位q1~q2的距离;
3.处于不同集合的p3~q3的距离;
但是问题处在第三步;
由于1,2相当于直接算两点距离,所以不涉及迭代问题;
对于二维平面下,计算则不是那么简单,很多BLog都提到再次细分矩阵的屁话,但是都没有解释为神马要细分;
对于上述合并过程,先求得left-mid和mid-right中的最小dmin,也就是前两种情况得最小值;
然后对先对mid点左右x-dmin和x+dmin得距离进行搜索,找到范围内的点;
之后看两点纵坐标是否满足相差小于dmin,如果满足,再计算距离;
这里最让人不能理解的有两点:
1.为神马按照dmin得范围来找;
其实细细想一下,我们最主要的目标是找到跨边界得两点距离小于dmin,那么根据勾股定理,则可以明显的知道:
对于c=a^2+b^2,c就是我们要找的最短距离,则如果a,b有一个大于等于dmin,则距离必定大于dmin;
2.为什么要把中心点定位mid:
合并思想使然,如果不定位mid,则会转化为左右半边得最小问题,也就是第一第二步得求解过程;
并且mid在代码过程中属于中介点,对于极端情况下,可以把所有潜在得两个分处不同子集得极端情况包含进去;
例如,mid点左右区域为2d,但是左右子集各有一点,如果距离小于d,极端情况下甚至水平,都能被包含进去;
典型题目:http://acm.hdu.edu.cn/showproblem.php?pid=1007
针对于该题目代码:
#include<iostream> #include<vector> #include<algorithm> using namespace std; struct point { double x; double y; }; const int maxn = 100100; const double INF = 10000000; int n; point num[maxn]; bool cmp1(point a, point b) { if (a.x != b.x) return a.x < b.x; return a.y < b.y; } bool cmp2(int a, int b) { return num[a].y < num[b].y; } double cnt(int a, int b) { return sqrt(pow(num[a].x - num[b].x, 2) + pow(num[a].y - num[b].y, 2)); } double charge(int l, int r) { if (l == r) return INF; if (l + 1 == r) { return cnt(l, r); } int mid = (r + l) / 2; double d1 = charge(l, mid); double d2 = charge(mid + 1, r); double d = min(d1, d2); //进行合并计算; vector<int>vec; for (int i = l; i <= r; i++) { if (fabs(num[i].x - num[mid].x) <= d) { vec.push_back(i); } } sort(vec.begin(), vec.end(), cmp2); for (int i = 0; i < vec.size(); i++) { for (int j = i + 1; j < vec.size()&&num[vec[j]].y - num[vec[i]].y < d; j++) { double dis = cnt(vec[i], vec[j]); if (dis < d) d = dis; } } return d; } int main() { while (cin >> n) { if (n == 0) break; for (int i = 0; i < n; i++) { cin >> num[i].x >> num[i].y; } sort(num, num + n, cmp1); printf("%.2lf\n", charge(0, n - 1) / 2); } return 0; }