二维几何-点集直径

点集直径,即给定点之间的最大距离。两两枚举的方法需要O(n^2)时间,并不是很优秀。

有一个办法可以更快的求出点集的直径。首先求点集的凸包,则最大距离一定来自于凸包上的两个顶点。由于凸包上的点的个数往往比原始点少很多,就算还是两两枚举,速度也比直接枚举快很多,当然最坏情况下时间复杂度仍是O(n^2),需要继续改进。

假设我们已经找到了直径,端点为Pi和Pj,现在我们分别从Pi和Pj出发各作一条垂直于PiPj的直线。

可以证明,整个凸包都被夹在了这两条直线中间。如果不然,不妨设Pi的下方有一个凸包的点P‘,则连接P’Pj,假设和下面那条直线交于Q,则P‘Pj>PjQ>PiPj 与 PiPj是直径矛盾,为了方便,我们把两条直线看成是有向直线,使得凸包位于两条直线的左侧。

像PiPj这样存在两条分别穿过这两个点的平行直线,把凸包夹在中间的点被称为对踵点对。给定一个角,有无数条以它为倾角的平行直线,但其中只有两条能把凸包紧紧的夹在中间,因此对踵点最多只有四对(一边两个点),这样,我们可以用一种称为旋转卡壳的方式找出所有对踵点对。

初始时,有两条有向直线把凸包夹在中间,一条水平向右,一条水平向左,这是可以求出初始对踵点为Pi和Pj(就是y座标最小和最大点)。接下来,逆时针旋转两条直线,看看对踵点对是如何变化的。假设穿过Pi的有向直线还需要逆时针旋转《二维几何-点集直径》i角度才能贴住边PiPi+1,类似定义《二维几何-点集直径》j。则当旋转的角度同时小于《二维几何-点集直径》i和《二维几何-点集直径》j时对踵点始终不变。

如果《二维几何-点集直径》i<《二维几何-点集直径》j,当旋转角度等于《二维几何-点集直径》i时,穿过pi的直线边会贴住PiPi+1,对踵点对中的Pi会变成Pi+1。同理,如果《二维几何-点集直径》i>《二维几何-点集直径》j,当旋转角度等于《二维几何-点集直径》j时穿过Pj的直线会贴住边PjPj+1,对踵点对中的Pj会变成Pj+1。如果《二维几何-点集直径》i=《二维几何-点集直径》j,则两条直线同时贴住新的边,因此Pi和Pj分别变成Pi+1和Pj+1。注意Pi和Pj+1、Pi+1和Pj是对踵点对,不要漏掉它们。重复这一过程,直到穿过Pi的直线倾角大于180度时终止。由于每次旋转至少会有一条直线贴出新的边,旋转过程的时间复杂度为O(n)

既然最远点对只会在对踵点中取到,只需在上述过程中每找到一对新的对踵点对时更新答案,就可以求出最远点对。

注意,上述算法知识一个概念描述,下面的代码是实用的写法。

首先给出辅助函数:

两点距离的平方.

int Dist(Point a, Point b)
{
    return (a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y);
}

求点集直径,返回点集直径的平方

int diameter(Point *p, int n)
{
    int u, v, ans;
    double diff;

    p[n] = p[0];    //不用取模了
    ans = 0;        //最大距离的平方
    for(u = 0, v = 1; u < n; u++)
        //一条直线贴住p[u]-p[u+1]
        for(;;)
        {
            /*
            当Area(p[u],p[u+1],p[v+1]) <= Area(p[u],p[u+1],p[v])时停止旋转
            即Cross(p[u+1]-p[u], p[v+1]-p[u]) - Cross(p[u+1]-p[u], p[v]-p[u]) <= 0
            而根据Cross(A,B)-Cross(A,C) = Cross(A,B-C) 得
            Cross(p[u+1]-p[u],p[v+1]-p[v]) <= 0
            */
            diff = Cross(p[u+1]-p[u], p[v+1]-p[v]);
            if(diff <= 0)
            {
                ans = max(ans, Dist(p[u], p[v]));         //u和v是对踵点
                if(diff == 0)                             //u和v+1也是对踵点b
                    ans = max(ans, Dist(p[u], p[v+1]));
                break;
            }
            v = (v+1) % n;                                //面积峯值也随着u的移动而移动
        }

    return ans;
}

 

 

    原文作者:算法
    原文地址: https://www.twblogs.net/a/5bd3a0582b717778ac209bde
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞