HDU 1542 Atlantis(扫描线求矩形面积并+离散化)
2015-08-14 22:53
405 查看
http://acm.hdu.edu.cn/showproblem.php?pid=1542
题意就是给n个矩形,这些矩形可能存在覆盖的地方,求矩形面积并。这种题目要用到扫描线,同时还要用到离散化(一时因为数据特别大,二是因为题目中存在浮点数)。
扫描线:一根虚拟的线,从下往上(或者从左往右)扫所求的图形,如下图所示,一条从下往上的直线扫到该4条线,并将图形分成三个部分分别来求面积。
扫描线解法(从下往上):
将扫到的边加入到segment结构体中,这个结构体中是这样的:
struct segment
{
double x1,x2,y;
int flag;
}arr
;
每个边存上左端点x1,右端点x2,以及该条线处于的y值。
同时要是该条边是矩形的下边,那么将flag置为1,上边置为-1(具体为什么下面再说)
按照y值将结构体排序,从小到大的顺序
将扫描到的线依次插入到线段树中并求值
线段树在扫描线算法中的意义:线段树的总区间如上图所示是整个图形在x轴上的投影。当扫描到n条线段时,我们只需要插入n-1条线段。刚开始一条线段都没插入时,整个线段树的每个节点的值都为0,每插入一条线段,这时候线段树记录的就是当前覆盖的线段长度,每插入一条线段时,就可以将ans更新,ans+=tr[1]*(arr[i+1].y-arr[i].y);
怎样计算当前覆盖的长度:这时候flag的作用体现出来了,同时还要借助一个cover数组用于记录当前线段树该节点是否被覆盖过的情况。刚开始,要memset(cover,0),每插入一条线段的时候,cover数组更新成:cover[i]+=flag;上面说了将一个矩形的下底边的flag置为1,将上边置为-1,当插入下底边的时候,当前节点的cover[i]=1,当再插入上边的时候,cover[i]+=-1此时就变成了0,和初始状态一样,这样就完美的完成了删除操作。再根据cover[i]的情况push_up;
介绍下push_up的操作:
如果当前节点的cover[i]==1,就说明当前该点表示的线段被覆盖了,那么tr[i] = 实际长度。
否则的话,说明这个点表示的长度并没有完全被覆盖,如果这个点是叶子节点,那么tr[i]=0,如果不是叶子节点,tr[i]就由下面的左右子结点推来。
关于push_up操作和cover数组要多理解 :)
题意就是给n个矩形,这些矩形可能存在覆盖的地方,求矩形面积并。这种题目要用到扫描线,同时还要用到离散化(一时因为数据特别大,二是因为题目中存在浮点数)。
扫描线:一根虚拟的线,从下往上(或者从左往右)扫所求的图形,如下图所示,一条从下往上的直线扫到该4条线,并将图形分成三个部分分别来求面积。
扫描线解法(从下往上):
将扫到的边加入到segment结构体中,这个结构体中是这样的:
struct segment
{
double x1,x2,y;
int flag;
}arr
;
每个边存上左端点x1,右端点x2,以及该条线处于的y值。
同时要是该条边是矩形的下边,那么将flag置为1,上边置为-1(具体为什么下面再说)
按照y值将结构体排序,从小到大的顺序
将扫描到的线依次插入到线段树中并求值
线段树在扫描线算法中的意义:线段树的总区间如上图所示是整个图形在x轴上的投影。当扫描到n条线段时,我们只需要插入n-1条线段。刚开始一条线段都没插入时,整个线段树的每个节点的值都为0,每插入一条线段,这时候线段树记录的就是当前覆盖的线段长度,每插入一条线段时,就可以将ans更新,ans+=tr[1]*(arr[i+1].y-arr[i].y);
怎样计算当前覆盖的长度:这时候flag的作用体现出来了,同时还要借助一个cover数组用于记录当前线段树该节点是否被覆盖过的情况。刚开始,要memset(cover,0),每插入一条线段的时候,cover数组更新成:cover[i]+=flag;上面说了将一个矩形的下底边的flag置为1,将上边置为-1,当插入下底边的时候,当前节点的cover[i]=1,当再插入上边的时候,cover[i]+=-1此时就变成了0,和初始状态一样,这样就完美的完成了删除操作。再根据cover[i]的情况push_up;
介绍下push_up的操作:
如果当前节点的cover[i]==1,就说明当前该点表示的线段被覆盖了,那么tr[i] = 实际长度。
否则的话,说明这个点表示的长度并没有完全被覆盖,如果这个点是叶子节点,那么tr[i]=0,如果不是叶子节点,tr[i]就由下面的左右子结点推来。
[code]#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algorithm> using namespace std; #define mset(x,y) memset(x,y,sizeof(x)) #define lson l,mid,i<<1 #define rson mid+1,r,i<<1|1 const int N = 200+10; double has[N*2];//离散化数组 double tr[N*8]; int cover[N*8]; struct seg { double x1,x2,y; int flag; friend bool operator < (const seg a,const seg b) { return a.y < b.y; }//将y值从小到大排序 }arr ; void pushup(int l,int r,int i)//very important! { if(cover[i]) tr[i]= has[r+1]-has[l]; else if(l==r) tr[i]=0; else tr[i]=tr[i<<1]+tr[i<<1|1]; return; } void update(int l,int r,int i,int a,int b,int flag) { if(l>=a&&r<=b) { cover[i]+=flag; pushup(l,r,i); return; } int mid=(l+r)>>1; if(mid>=a) update(lson,a,b,flag); if(mid<b) update(rson,a,b,flag); pushup(l,r,i); return; } int main() { int n,cas=1; while(~scanf("%d",&n)) { if(n==0) break; int t=0; for(int i=0;i<n;i++) { double x1,y1,x2,y2; scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2); arr[t].x1=x1,arr[t].x2=x2,arr[t].y=y1,arr[t].flag=1; //扫描边 has[t]=x1; t++; arr[t].x1=x1,arr[t].x2=x2,arr[t].y=y2,arr[t].flag=-1; has[t]=x2; t++; } sort(arr,arr+t); sort(has,has+t);//离散化操作 double ans=0; mset(tr,0);mset(cover,0); for(int i=0;i<t-1;i++) { int xx = lower_bound(has,has+t,arr[i].x1) - has ; int yy = lower_bound(has,has+t,arr[i].x2) - has - 1; if(xx<=yy) update(0,t-1,1,xx,yy,arr[i].flag); ans += tr[1]* (arr[i+1].y-arr[i].y);//更新ans } printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++,ans); } return 0; }
关于push_up操作和cover数组要多理解 :)
相关文章推荐
- 常用数据结构_排序_查找练习
- android 自定义控件 圆形图片
- 九度 Online Judge 算法 刷题 题目1065:输出梯形
- Android 开发中用到的几个多线程解析(代码示例)
- 文件动态打开获取数据
- Effective Objective-C Notes:GCD 实现同步锁
- PPT里面实现动态图表
- Cocos2dx-Tiled Map(瓦片地图)
- 集合小结
- 修改配置Eclipse提示代码以及空格上屏
- 网络编程03---JSON和XML
- 华为机试 是否存在路径(深度优先遍历、回朔法、递归)
- google-GSON解析和生成JSON数据
- POJ 2406
- 生产dump文件
- 深入分析 Java 中的中文编码问题
- 邮件系统相关协议之POP
- malloc calloc 和 realloc
- ARC 下内存泄露的那些点
- 【我的技术我做主】IT屌丝DIY打造6盘位家用NAS服务器