[洛谷P1382] 楼房
题目描述
地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i])。地平线高度为0。在轮廓线长度最小的前提下,从左到右输出轮廓线。
下图为样例2。

输入输出格式
输入格式:
第一行一个整数n,表示矩形个数
以下n行,每行3个整数h[i],l[i],r[i]表示第i个矩形。
输出格式:
第一行一个整数m,表示节点个数
以下m行,每行一个坐标表示轮廓线上的节点。从左到右遍历轮廓线并顺序输出节点。第一个和最后一个节点的y坐标必然为0。
输入输出样例
2
3 0 2
4 1 3
6
0 0
0 3
1 3
1 4
3 4
3 0
5
3 -3 0
2 -1 1
4 2 4
2 3 7
3 6 8
14
-3 0
-3 3
0 3
0 2
1 2
1 0
2 0
2 4
4 4
4 2
6 2
6 3
8 3
8 0
说明
【数据范围】
对于30%的数据,n<=100
对于另外30%的数据,n<=100000,1<=h[i],l[i],r[i]<=1000
对于100%的数据,1<=n<=100000,1<=h[i]<=10^9,-10^9<=l[i]<r[i]<=10^9
先留一点思考的时间
题解:
题意就是给出一张图形的坐标 ,求出这张图中的拐点个数和坐标.
对于60%的数据,可以直接模拟加离散化. 用数组存下每一个X轴上的点的最高值,但是为什么要用离散化呢?
当遇到这样的情况时,代码就会出问题:

h[2]=3, h[3]=2,那么在循环扫过来时就不会处理(2,0)和(3,0),但这两个点是存在的,所以要用离散化,将一格坐标变为两格坐标:

这样的话循环只会多一点常数,但是答案能保证正确.
对于100%的数据:
可以用离散化加线段树或扫描线.
离散化加线段树的方法与暴力相类似,用线段树加速了区间最值的修改与查询.
扫描线
将矩形的左右两边看作是一条线段,分别是一个矩形的入边和出边,然后通过排序确定遍历扫描线的顺序.这样就可以在O(nlogn)的时间复杂度内完成.
那么扫描线应该怎么排序呢?
我们用扫描线就是为了确定一个矩形的覆盖情况,然后找到图中的拐点.那么首先就应该是要按照X轴上的坐标进行排序.并且应该先扫入边,再扫出边,这样可以保证不会有什么鬼的奇怪的数据有左右边相同的矩形使得结果出问题. 在判断完前两种情况后,现在正在排序的两条线就同属于一种边(同为入边或出边),那么若是入边,则越高越容易挡住另一个矩形,就要放在前面才会使得更新一次答案时没有矩形被挡住.若是出边,则越低越容易被挡住,则要放在后面,这样就可以使得高的出边在后面被扫到,就可以保证在删除最后一条出边时更新答案没有矩形被挡住.
bool cmpl(line a,line b){
if(a.x!=b.x) return a.x<b.x;
if(a.f!=b.f) return a.f<b.f;
if(a.f==) return a.h>b.h;
if(a.f==) return a.h<b.h;
}
然后在后面扫描的时候就直接用for循环遍历每一条扫描线.那么扫到一条扫描线就有两种情况:
- 属于入边,如果比当前最高值还要高,则出现了拐点,更新答案.
- 属于出边,如果高度为当前最大高度且该高度出边只有一条,则出现了拐点,更新答案.
这里记录当前已经加入扫描的矩形时用了一个STL容器:multiset.这个东西与set不同的地方体现在它可以存入相同的元素,且默认单调递增,那么对于这个题目的实现就提供了一个很好的帮助.
C++ STL MultiSet类成员函数列表如下:
clear() 清除所有元素
count() 返回指向某个值元素的个数
end() 返回指向最后一个元素的迭代器
equal_range() 返回集合中与给定值相等的上下限的两个迭代器
erase() 删除集合中的元素
find() 返回一个指向被查找到元素的迭代器
insert() 在集合中插入元素
key_comp() 返回一个用于元素间值比较的函数
lower_bound() 返回指向大于(或等于)某值的第一个元素的迭代器
rbegin() 返回指向多元集合中最后一个元素的反向迭代器
rend() 返回指向多元集合中第一个元素的反向迭代器
size() 多元集合中元素的数目
upper_bound() 返回一个大于某个值元素的迭代器
value_comp() 返回一个用于比较元素间的值的函数
然后把答案存到数组里,根据题目要求输出.
下面是代码:
#include<bits/stdc++.h>
using namespace std;
const int N=; int n;
int cnt=;
int cnta=; struct line{
int x,h,f;
}l[N*]; struct answer{
int x,y;
}ans[N*]; int gi(){
int ans=,f=;char i=getchar();
while(i<''||i>''){if(i=='-')f=-;i=getchar();}
while(i>=''&&i<=''){ans=ans*+i-'';i=getchar();}
return ans*f;
} bool cmpl(line a,line b){
if(a.x!=b.x) return a.x<b.x;
if(a.f!=b.f) return a.f<b.f;
if(a.f==) return a.h>b.h;
if(a.f==) return a.h<b.h;
} int main(){
//freopen("Fort.in","r",stdin);
//freopen("Fort.out","w",stdout);
n=gi();
for(int i=,x,y,z;i<=n;i++){
x=gi(); y=gi(); z=gi();
l[++cnt].x=y; l[cnt].h=x; l[cnt].f=;
l[++cnt].x=z; l[cnt].h=x; l[cnt].f=;
}
sort(l+,l+cnt+,cmpl);
multiset <int> s; s.insert();
for(int i=;i<=cnt;i++){
int maxh=*s.rbegin();
if(l[i].f==){
if(l[i].h>maxh){
ans[++cnta].x=l[i].x; ans[cnta].y=maxh;
ans[++cnta].x=l[i].x; ans[cnta].y=l[i].h;
}
s.insert(l[i].h);
}
if(l[i].f==){
if(l[i].h==maxh&&s.count(maxh)==){
s.erase(maxh);
ans[++cnta].x=l[i].x; ans[cnta].y=l[i].h;
ans[++cnta].x=l[i].x; ans[cnta].y=*s.rbegin();
}
else s.erase(s.find(l[i].h));
}
}
cout<<cnta<<endl;
for(int i=;i<=cnta;i++)
printf("%d %d\n",ans[i].x,ans[i].y);
return ;
}
[洛谷P1382] 楼房的更多相关文章
- 洛谷P4198 楼房重建 (分块)
洛谷P4198 楼房重建 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题, ...
- 洛谷 P4198 楼房重建 题解
题面 首先你要知道题问的是什么:使用一种数据结构,动态地维护以1为起点地最长上升子序列(把楼房的高度转化成斜率地序列)的长度: 怎么做?线段树! 我们在线段树上维护两个东西:1.这个区间内斜率的最大值 ...
- 洛谷P4198 楼房重建 单调栈+线段树
正解:单调栈+线段树 解题报告: 传送门! 首先考虑不修改的话就是个单调栈板子题昂,这个就是 然后这题的话,,,我怎么记得之前考试好像有次考到了类似的题目昂,,,?反正我总觉着这方法似曾相识的样子,, ...
- 洛谷P4198 楼房重建(线段树)
题意 题目链接 Sol 别问我为什么发两遍 就是为了骗访问量 这个题的线段树做法,,妙的很 首先一个显然的结论:位置\(i\)能被看到当且仅当\(\frac{H_k}{k} < \frac{H_ ...
- 洛谷P4198 楼房重建
题意:给定序列,每次修改一个值,求前缀最大值的个数. 解:线段树经典应用. 每个节点维护最大值和该区间前缀最大值个数. 发现我们不用下传标记,只需要合并区间. 需要实现一个函数int ask([l r ...
- 洛谷 P4198 楼房重建
思路 此题可转化为以下模型 给定序列\(a[1...n]\),支持单点修改,每次求区间单调栈大小 \(n,Q\le 10^5\) 区间单调栈是什么呢?对于一个区间,建立一个栈,首先将第一个元素入栈,从 ...
- 楼房 洛谷1382 && codevs2995
P1382 楼房 题目描述 地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i]).地平 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
随机推荐
- Hive数据倾斜和解决办法
转自:https://blog.csdn.net/xinzhi8/article/details/71455883 操作: 关键词 情形 后果 Join 其中一个表较小,但是key集中 ...
- 多表头的DataGridView
上次在程序中要用到多表头的DataGridView,在网上搜索了一个,感觉还不错,现在简单的介绍一下它的用法.首先得把这个dll拷贝到相应的目录下,dll名称是myMultiColHea ...
- Scala学习笔记(一):环境搭建
计算机领域的编程语言种类繁多,如C.C++.Java.C#等,我们知道的一般都是较为流行的编程语言,然有更多的是没听说过的,于是也就说不上关注或者使用了 一次在网上查资料时,无意间看到了“函数式编程” ...
- springmvc基础篇—处理图片静态资源文件
当我们在web.xml中对DispatcherServlet的过滤设置为/ 的时候,表示对所有的路径进行拦截过滤,那么不可避免的就会产生一个问题,那就是像图片这种静态资源文件我明明引用路径有,但就是加 ...
- 失败的尝试,使用继承扩展数组,以及ES6的必要性
我们都知道直接在原生对象上扩展对象是很不好的.所以prototype这样的库广受非议. 一些库,比如lodash采用了工具包形式的扩展方式,绕开了对象的继承. 由于es6的class的出现,我尝试以A ...
- C++学习013多态
何为多态 面向对象最要的特征之一就是多态,而纯虚函数是实现多态的主要方式.它可以提供一个通过用的接口,同样调用一个方法, 由于运算对象不同,方法也不同,这也就是所谓的动态绑定. #include &l ...
- 系统学习Docker 践行DevOps理念
Docker代表的容器技术是近两年的大热技术,和人工智能.区块链等热点不同,容器技术的门槛并不高,每一个开发.测试.运维人员都能在日常工作中掌握和使用,是当今IT从业人员的必备技能之一.本课程会带大家 ...
- Visual Studio 2013安装包
点击下载
- HDU 4468 Spy(KMP+贪心)(2012 Asia Chengdu Regional Contest)
Description “Be subtle! Be subtle! And use your spies for every kind of business. ”― Sun Tzu“A spy w ...
- 【iOS开发】NSOperation简单介绍
iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...