[翻译]扫描线算法(Line Sweep Algorithm)(1)

原地址:https://www.hackerearth.com/zh/practice/math/geometry/line-sweep-technique/tutorial/
在cf评论区偶然看见的,顺便学习一下好了……然后发现是个大坑
我英语比较菜,如有谬误还请纠正……


在这篇文章,我们将会学到一些基于计算几何的算法。
扫描线是一条想象中的向右扫过平面的竖直线。也因此,以此思想为基础的算法也被称为平面扫描算法(Plane Sweep Algorithm),我们以某些事件为基础扫描我们思考的问题以使扫描离散化。
这些事件都是以我们思考的问题为基础,我们将在下面讨论的算法中看见。除去这些事件以外,我们需要维护一些数据结构来储存以y坐标为顺序排列的点(这一顺序有时可能会改变)以助益于在扫描到某些事件时进行操作。在一些情况,该数据结构只储存活动事件。
另一个需要注意的事情是,这种算法的效率取决于我们选用的数据结构。一般地,我们可以用C++中的set,但有时可能我们需要储存更多东西,所以我们可能采用平衡二叉树。


让我们思考我们的第一个算法。

最近的点(Closest Pair)

问题:从互不相同的N个点中找出最近的两个。

这个问题可以通过暴力枚举每两个点解决,不过这样复杂度为 O(N2) 。因此我们需要更好的算法,这里我们使用扫描线算法。

对这个问题我们可以考虑在数组中作为事件的的点。在一个set中,我们将已经访问过的点按y坐标排列,因此在从左向右扫描时首先我们以x坐标排序。
现在想象我们已经扫描过1至N-1,令h为我们得到的最短距离。对第N个点,我们想要找到距离小于等于h的点,而对于横坐标,只有横坐标范围在 [xNh,x] 内,纵坐标范围在 [xNh,xN+h] 内的才是我们需要担心的点。这些就是对于set的活动事件,在set中所有横坐标小于 xNh 的都会被删除。接下来我们再向set加入第N个点。
一个需要注意的事情是,对许多情况,是活动事件的点的数量是O(1)的(除该点本身外最多可以有5个点是活动的)(不是很懂这句,原句:One thing to note is that at any instance, the number of points which are active events is O(1)(there can be atmost 5 points around a point which are active excluding the point itself))
好的,理论已经够多了,让我们看看这个算法的运作。
这里写图片描述
这里写图片描述
将h初始化为开始的两个点,若现在的距离小于h则更新h的值。


图片中红色区域包括的点是被当前点评估的点。这些点左侧是已经被移除set的点。
下面是上述算法的cpp代码:

#define px second
#define py first
typedef pair<long long, long long> pairll;
pairll pnts [MAX];
int compare(pairll a, pairll b)
{ 
        return a.px<b.px; 
}
double closest_pair(pairll pnts[],int n)
{
        sort(pnts,pnts+n,compare);
        double best=INF;
        set<pairll> box;
        box.insert(pnts[0]);
        int left = 0;
        for (int i=1;i<n;++i)
        {
            while (left<i && pnts[i].px-pnts[left].px > best)
                box.erase(pnts[left++]);
            for(typeof(box.begin()) it=box.lower_bound(make_pair(pnts[i].py-best, pnts[i].px-best));it!=box.end() && pnts[i].py+best>=it->py;it++)
                best = min(best, sqrt(pow(pnts[i].py - it->py, 2.0)+pow(pnts[i].px - it->px, 2.0)));
            box.insert(pnts[i]);
        }
        return best;
}

让我们来分解一下上述代码:
1.首先,我们以x为参数排序了点的数组
2.之后我们将数组pnts的第一个点加入了set中。注意,我们已经将纵坐标作为pair的第一个参数,所以set将以y坐标为参数排列。
3.在第一重循环的第一个子循环中,对每个pnts中的点,我们清除掉 [XNh,XN] 以外的点,这一过程是O(N)的因为我们只有N个元素。由于删除的复杂度是O(logn),所以这一系列操作的复杂度是O(NlogN)
4.在第二个子循环中,我们遍历了所有所有横坐标在 [xNh,x] 内,纵坐标范围在 [xNh,xN+h] 内的点,找到lower_bound是logn的,而这一循环最多进行5次。
5.将每个点插入至set中,O(logn)
综上所述,时间复杂度为O(NlogN)。接下来让我们看下一个问题。

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值