关于平面最近两点问题

最简单的思想是直接进行遍历,也就是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;
}

猜你喜欢

转载自www.cnblogs.com/songlinxuan/p/12666064.html
今日推荐