[翻译]扫描线算法(Line Sweep Algorithm)(2)
来源:互联网 发布:索尼手机更新软件 编辑:程序博客网 时间:2024/06/06 02:17
NOIPD110分滚粗。
心累。
学点有趣的治愈一下。
突然想起似乎之前还有个坑没有填,就练一波英语阅读。
矩形面积交
给出一个集合包含N个与坐标轴对称的矩形(矩形的边与x轴、y轴平行),找到所有的矩形的重叠部分。其中一个矩形由两个点代表,一个是左下角的点,一个是右上角的点。
这个问题的事件,是垂直的边。当我们遇到一条左边,我们进行一些操作;遇到一条右边,进行另一些操作。左边由左下角来代表,右边由右上角代表。
我们以对x坐标的排序来开始整个算法。当一个长方形左下角的点被遇到(也就是说我们遇到了长方形的左边),我们将之插入到集合中。当我们遇到了右上角的点(也就是遇到了长方形的右边),我们将长方形从集合中清除出去。在任何情况,集合中只存在被扫描线扫到的矩形(即已遇到左边但未遇到右边)。
扫到的面积是Δy*Δx,其中Δy是扫描线被矩形切割的长度(下图中的红色部分),Δx为两个扫描线事件之间的距离。
但是现在我们只知道哪些是被扫描线被切割的矩形。因此,这里我们有一个新的问题:如何找到被切割的最大长度?
其做法与我们刚才(参见上一篇)做的类似。我们仍然用扫描线技术,不过这条线现在要旋转90°,换言之,我们从下到上去扫描被扫到的矩形。我们用一个变量cnt维护当前重叠的矩形数量。当我们遇到一条底边就让cnt+1,遇到一条上边就让cnt-1,当cnt从非0变为0时我们就找到了扫描线被切割的长度。
下面来看看它是如何运行的。
上面的图像向我们展示了水平扫描线的运行过程, Δy 就是最后一张图片展示的两个箭头长度之和。
这就是我们的算法,接下来考虑枚举的部分。对于每一个扫描线的事件,我们需要找到扫描线切出的长度,也就是推进水平扫描线长度。这里以bool数组作为数据结构,因为我们可以分别以横、纵来做一次排序。
下面是cpp代码:
#define MAX 1000struct event { int ind; // 众多矩形中该矩形的索引 bool type; // 事件类型,0表示左下,1表示右上 event() {}; event(int ind, int type) : ind(ind), type(type) {};};struct point { int x, y;};point rects [MAX][12]; // 每个矩形包含两个顶点: [0]=左下;[1]=右上bool compare_x(event a, event b) { return rects[a.ind][a.type].x<rects[b.ind][b.type].x; }bool compare_y(event a, event b) { return rects[a.ind][a.type].y<rects[b.ind][b.type].y; }int union_area(event events_v[],event events_h[],int n,int e){ //n是矩形数量, e=2*n , e是顶点数量(在矩形集合中每个矩形以两个点被申明) bool in_set[MAX]={0}; int area=0; sort(events_v, events_v+e, compare_x); //竖直边的预处理 sort(events_h, events_h+e, compare_y); // 水平边的预处理 in_set[events_v[0].ind] = 1; for (int i=1;i<e;++i) { // 水平扫描线 event c = events_v[i]; int cnt = 0; // 表明有多少矩形重叠的计数器 // Delta_x: 当前线与之前线的距离 int delta_x = rects[c.ind][c.type].x - rects[events_v[i-1].ind][events_v[i-1].type].x; int begin_y; if (delta_x==0){ in_set[c.ind] = (c.type==0); continue; } for (int j=0;j<e;++j) if (in_set[events_h[j].ind]==1) //当前矩形的水平扫描线 { if (events_h[j].type==0) //是底边 { if (cnt==0) begin_y = rects[events_h[j].ind][0].y; // 某一块开始 ++cnt; //重叠矩形增加量 } else //是上面的线 { --cnt; //矩形已经不被重叠,所以移除之 if (cnt==0) //一块的结束 { int delta_y = (rects[events_h[j].ind][13].y-begin_y);//竖直扫描线被切割的长度 area+=delta_x * delta_y; } } } in_set[c.ind] = (c.type==0);//如果是左边, 矩形在当前集合中,否则不在 } return area;}
复杂度显然是O(n^2)。如果用其它数据结构(如BST)维护,可以降到O(nlogn)
现在,你已经多少了解这项技术了,不是吗?(并不)
让我们去下一个可以用这项技术解决的问题吧。(没错,就是凸包)
- [翻译]扫描线算法(Line Sweep Algorithm)(2)
- [翻译]扫描线算法(Line Sweep Algorithm)(1)
- 轮廓线扫描算法:Theo Pavlidis' Algorithm
- Sweep and Prune Algorithm - Introduction
- 标记-清除( Mark-Sweep )算法
- 扫除算法(sweep)求逆矩阵
- 扫除算法(sweep)求逆矩阵
- STL algorithm算法all_of的翻译及使用(2)
- PhysX官方手册翻译 - 扫掠API(Sweep API)
- Graham扫描算法(Graham Scan Algorithm)
- sweep算法计算intersect
- Mark-Sweep算法
- 扫描线填充算法(2)
- uva 972 - Horizon Line(平移扫描线)
- Garbage Collection | Mark-Sweep算法
- Sweep
- 算法(Algorithm)
- 算法(Algorithm)
- hdu 6180 Schedule(贪心)
- kmalloc和vmalloc的区别和联系
- 2017/8/25
- [OpenGL] 网格细分算法 Loop Subdivision
- 数据结构——线性结构(1)——顺序栈的实现
- [翻译]扫描线算法(Line Sweep Algorithm)(2)
- 训练总结8.25
- 数学杂题 鸡兔同笼
- 创建视图
- virt-install命令参数
- HDU6180 Schedule
- 编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad
- win7下启动activeMQ服务
- Java 多线程间的通信 等待唤醒