题意:

  给你n个矩形,输入每个矩形的左上角坐标和右下角坐标。

  然后求矩形的总面积。(矩形可能相交)。

题解:

前言

  先说说做这道题的感受:

  刚看到这道题顿时就懵逼了,几何 烂的渣渣。后来从网上搜题解。才知道用到线段树+离散化+扫描线。不过这是我第一次接触扫描线,根本不知道什么鬼啊。后来各种博客和论文看了一天才真正理解。

  不过一想到网上的博客和论文,就来气。都什么啊,代码注释少的很而且说不明白什么意思,比如线段树怎么存每个节点的数据?为什么这么存?每个节点的数据变量都什么意思?更新的时候怎么更新?意思是啥?都TM没写明白,还得老子自己慢慢悟才想明白到底咋回事。

  所以为了让更多的同学明白这道题怎么做,线段树+离散化+扫描线到底是个啥,特此写下这篇博客!

正文:

1、从理论角度如何解这道题

扫描线的用法:

  由于画图太麻烦了,直接用别人的图吧...

  分割线里面的内容转载自http://hzwer.com/879.html

-------------------------------------------------分割线-------------------------------------------------------------------------------------------------------

    

顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在写代码的过程中,这根线很重要。方向的话,可以左右扫,也可以上下扫。方法是一样的,这里我用的是由下向上的扫描法。

如上图所示,坐标系内有两个矩形。位置分别由左下角和右上角顶点的坐标来给出。上下扫描法是对x轴建立线段树,矩形与y平行的两条边是没有用的,在这里直接去掉。如下图。

现想象有一条线从最下面的边开始依次向上扫描。线段树用来维护当前覆盖在x轴上的线段的总长度,初始时总长度为0。用ret来保存矩形面积总和,初始时为0。

由下往上扫描,扫描到矩形的底边时将它插入线段树,扫描到矩形的顶边时将底边从线段树中删除。而在代码中实现的方法就是,每条边都有一个flag变量,底边为1,顶边为-1。

用cover变量(通过线段树维护)来表示某x轴坐标区间内是否有边覆盖,初始时全部为0。插入或删除操作直接让cover += flag。当cover > 0 时,该区间一定有边覆盖。

开始扫描到第一条线,将它压入线段树,此时覆盖在x轴上的线段的总长度L为10。计算一下它与下一条将被扫描到的边的距离S(即两条线段的纵坐标之差,该例子里此时为3)。

则 ret += L * S. (例子里增量为10*3=30)

结果如下图

  橙色区域表示已经计算出的面积。

扫描到第二条边,将它压入线段树,计算出此时覆盖在x轴上的边的总长度。

例子里此时L=15。与下一条将被扫描到的边的距离S=2。 ret += 30。 如下图所示。

绿色区域为第二次面积的增量。

接下来扫描到了下方矩形的顶边,从线段树中删除该矩形的底边,并计算接下来面积的增量。如下图。

 蓝色区域为面积的增量。

此时矩形覆盖的总面积已经计算完成。 可以看到,当共有n条底边和顶边时,只需要从下往上扫描n-1条边即可计算出总面积。

此题因为横坐标包含浮点数,因此先离散化。另外,因为用线段树维护的是覆盖在x轴上的边,而边是连续的,并非是一个个断点,因此线段树的每一个叶子结点实际存储的是该点与下一点之间的距离。

--------------------------------------------------分割线-------------------------------------------------------------------------------------------------------------------------------

我的理解:

线段树存的是在x轴上线段区间的覆盖情况。

把矩形的上下边拆分成一条一条的线,每个左右边看成是x轴方向一个一个的点。把这些上下边和点都按升序排序。然后用传说中的扫描线从下往上挨个扫描每条上下边。

当遇到一条边时,在线段树中把这条边覆盖的每个线段打上标记,cover+=flag。(线结构体里的flag变量标识该条线段是上边还是下边,为1是下边,-1是上边),当每个节点的cover==0时,说明这个节点所掌控的线段范围没有被覆盖或没有完全被覆盖,cover>0时,被覆盖。

更新完之后,然后用(即将扫描到的下一条边的高度-当前边的高度)*x轴上线段区间被覆盖的长度,就是当前所计算的面积。然后把这些面积加在一起就是总面积。

由于横坐标范围太大,要离散化之后再在线段树中存储。(离散化自己百度,不难)。

那么线段树怎么存线段呢?比如1~10长度的线段,怎么存,每个节点都有什么意义?看下图:

存1~10每个区间的长度。

为什么这么存呢?看下图:

这样每个编号对应一段单位线段。所以1~3的长度是编号1,2所代表的线段的长度。

这只是一个简单的例子。

明白了这些,来看下代码:

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>using namespace std;const int MAX=5000;typedef struct node    //树节点结构体
{node(){l=r=len=flag=0;}int l,r;double len;       //如果该区间下有被覆盖的线段,len表示被覆盖的区域的长度int flag;         //标识是否被覆盖
}TreeNode;typedef struct line         //边 结构体,f=1为下边,f=-1为上边
{line(){a=b=h=f=0;}double a,b;          //a,b分别表示这条线的左右横坐标double h;            //h表示这条线段所在的高度int f;               //标识是上边还是下边
}Line;double a[MAX];int n;
int line_len;            //有line_len条边
int a_len;               //有a_len个横坐标
TreeNode tNode[MAX<<4];
Line line[MAX];void buildTree(int p,int l,int r)
{tNode[p].l=l;tNode[p].r=r;tNode[p].flag=0;              //标识是否被覆盖tNode[p].len=0;               //如果该区间下有被覆盖的线段,len表示被覆盖的区域的长度if(l==r)return;int mid=(l+r)>>1;buildTree(p<<1,l,mid);buildTree(p<<1|1,mid+1,r);return;
}void getLen(int p)       //计算当前节点被覆盖的长度
{if(tNode[p].flag)    //如果当前节点被完全覆盖{tNode[p].len=a[tNode[p].r+1]-a[tNode[p].l];     //每个节点代表的是当前节点到下一个节点的长度}else if(tNode[p].l==tNode[p].r)   //叶子节点,单位线段,如果当前节点没有被覆盖或者没有被完全覆盖,那么就是没有给覆盖{tNode[p].len=0;}elsetNode[p].len=tNode[p<<1].len+tNode[p<<1|1].len;   //不是叶子节点,不知道覆盖情况,从孩子节点获取信息
}void update(int p,int l,int r,int val)      //更新线段树  val为-1表示覆盖一层,否则删除一层
{if(tNode[p].l>=l&&tNode[p].r<=r)        //不必更新到底,因为每条上下边都是对应的,添加删除不影响其他节点,而且所需要的信息是第一个节点的信息。{tNode[p].flag+=val;getLen(p);return;}if(tNode[p].l>r||tNode[p].r<l){return;}update(p<<1,l,r,val);update(p<<1|1,l,r,val);getLen(p);return;
}int binary(double key,int low,int high)   //二分查找每个横坐标离散化之后所对应的编号。建树的时候要用这些编号建树,不是用实际的坐标
{while(low<=high){int mid=(low+high)>>1;if(a[mid]==key)return mid;if(key<a[mid]){high=mid-1;}if(key>a[mid]){low=mid+1;}}return -1;
}bool compare(Line a,Line b)
{return a.h<b.h;
}int main()
{int n;int Case=1;while(cin>>n&&n){line_len=0;a_len=0;memset(a,0,sizeof(a));double x1,y1,x2,y2;for(int i=0;i<n;i++){cin>>x1>>y1>>x2>>y2;//横坐标的每个点赋值a[a_len++]=x1;a[a_len++]=x2;//每条线赋值line[line_len].a=x1;line[line_len].b=x2;line[line_len].h=y1;line[line_len].f=-1;line[line_len+1].a=x1;line[line_len+1].b=x2;line[line_len+1].h=y2;line[line_len+1].f=1;line_len+=2;}sort(a,a+a_len);   //把所有横坐标排序int num=1;for(int i=1;i<a_len;i++)  //排除重复的{if(a[i]!=a[i-1]) a[num++]=a[i];}buildTree(1,0,num-1);     //排除重复的之后由num个横坐标,所以建树范围是0~num-1,也可以是1~num。随便sort(line,line+line_len,compare);   //所有的边所处高度排序double sum=0;for(int i=0;i<line_len-1;i++)    //遍历排序完的边{update(1,binary(line[i].a,0,num-1),binary(line[i].b,0,num-1)-1,line[i].f);   //先更新覆盖情况sum+=(line[i+1].h-line[i].h)*tNode[1].len;             //计算面积}printf("Test case #%d\n",Case);printf("Total explored area: %.2lf\n\n",sum);Case++;}return 0;
}

  

先要把这个理解,再看代码,不然看也看不懂,慢慢理解,不能急。

转载于:https://www.cnblogs.com/f-society/p/6728026.html

hdu1542 矩形面积并(线段树+离散化+扫描线)相关推荐

  1. 【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)

    [POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS   Memory Limit: 65536K Total Submiss ...

  2. poj 1177 线段树+离散化+扫描线 求矩形并的轮廓长

    Picture 题意:求矩形面积并额轮廓长 解法:扫描线+离散化+线段树 做法:等于更新操作前后的tree[1].len差,做法这么巧妙实际我也不知道为什么.差不多的意思就是更新操作的cover变化长 ...

  3. HDU 1542 Atlantis 线段树+离散化+扫描线

    题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #inclu ...

  4. poj--1177(线段树+离散化+扫描线)

    题目:http://poj.org/problem?id=1177 A number of rectangular posters, photographs and other pictures of ...

  5. POJ1177 Picture 线段树+离散化+扫描线

    求最终的覆盖图形周长,写这种代码应该短而精确,差的比较远 /* Problem: 1177 User: 96655 Memory: 348K Time: 32MS Language: C++ Resu ...

  6. 线段树辅助——扫描线法计算矩形面积并

    线段树辅助--扫描线法计算矩形面积并 本篇文章转自:传送门 分析: 1.矩形比较多,坐标也很大,所以横坐标需要离散化(纵坐标不需要),熟悉离散化后这个步骤不难,所以这里不详细讲解了,不明白的还请百度 ...

  7. HDU 1828 线段树之扫描线之周长并

    点击打开链接 题意:给n个矩形,求它们重叠后的周长 思路:用线段树的扫描线从下到上扫一遍,与面积并思想有些相似面积并,下面重边的处理相似,但是周长的并需要求的是竖边的个数然后乘以高度,而面积并求的是底 ...

  8. poj2528贴海报(线段树离散化)

    //poj2528贴海报(线段树离散化) #include<cstring> #include<iostream> #include<cstdio> #includ ...

  9. poj 2528 Mayor's posters(线段树+离散化)

    1 /* 2 poj 2528 Mayor's posters 3 线段树 + 离散化 4 5 离散化的理解: 6 给你一系列的正整数, 例如 1, 4 , 100, 1000000000, 如果利用 ...

  10. HDOJ 2492 Ping pong 线段树+离散化

    //2492 Ping pong 线段树+离散化 /* 题意: 有一陀人从左到右排成一排,每个人有一个唯一的技能值,每个人都找其他人比赛, 比赛前要再找一个人做裁判,裁判的技能值不能比这两个人都高,也 ...

最新文章

  1. OpenCV实现遍历文件夹下所有文件
  2. 《机器学习》周志华-CH2 模型评估与选择
  3. [转]JAVA泛型通配符T,E,K,V区别,T以及ClassT,Class?的区别
  4. Codeforces 1016F Road Projects
  5. Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?
  6. 校内训练赛题解第三篇
  7. 中文-自然语言处理-开源工具-流行度调查+句法依存树可视化调研
  8. % mysql 代表_MySQL中的星号(*)和百分号(%)代表表示什么,MySQL的库名、表名、字段名、字段值是否区分大...
  9. Flink keyby 数据倾斜问题处理
  10. nginx main error_page
  11. LeetCode 5353. 灯泡开关 III
  12. 将markdown文本转换为微信文章格式的解决方案
  13. TIOBE 3 月编程语言排行榜:Java 大涨,Delphi 辉煌不再!
  14. 【My Sft】彩色图像分析软件V1.0.1.0
  15. centos 安装tmux
  16. 使用蒲公英工具箱获取设备 UDID
  17. 计算机任务管理器无法响应,Win7系统电脑在任务管理器中关闭进程时总是未响应的解决方法...
  18. Vue中使用微信JSDK实现图片上传
  19. 【华人学者风采】马帅 北京航空航天大学
  20. epub电子书如何用Windows电脑打开?

热门文章

  1. mongodb的mapReduce查询
  2. mysql一对多查询合并多的一方的数据。
  3. 20170317起mvc功能学习
  4. Linux下squid代理缓存服务环境部署
  5. Hibernate学习(一)创建数据表
  6. [傅里叶变换及其应用学习笔记] 二十二. 快速傅里叶变换
  7. JFinal实现原理
  8. 读书笔记:陈希孺:概率论与数理统计:2014.01.01
  9. php用date表示本月,用php获取本周,上周,本月,上月,本季度日期的代码
  10. qt 无框dockwidget_QT解决通过菜单方式无法让QDockWidget 显示和隐藏