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

来源:互联网 发布:商城系统数据库设计 编辑:程序博客网 时间:2024/06/03 20:07

原地址: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 firsttypedef 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)。接下来让我们看下一个问题。

原创粉丝点击