您的位置:首页 > 理论基础 > 计算机网络

HDU 5033 Building(DP,2014北京网络赛1002)

2014-09-21 23:04 246 查看
题目:Building

题意:坐标轴上N个建筑物,忽略宽度只计高度,然后查询的是,当人的坐标为qi时,人抬头能看到的天空的角度有多大。即有多大的角度是没有被建筑物遮挡到的。人的高度视为0。

一看到这题就想到了单调队列维护个最优方案,最后写出来其实是个单调栈。这个保证从栈底到栈顶是建筑物高度从小到大从大到小的。

对于这个问题,我们可以分成两边处理。求出左边被遮挡的角度,再求出右边被遮挡的角度,用180减去这两个就是答案。而左边会求,右边自然也会求。

把查询都读入之后,放一个结构体,id为0表示建筑物,大于0的对应查询的编号。按照x从小到大排序。

接着从左往右扫,遇到建筑物的,做添加,添加的时候维护下栈:

当前栈顶元素是top-1的位置,top=0表示空。如果当前的建筑物比栈顶的高,那肯定当前建筑物更有可能挡住后面的查询,删除栈顶元素直到它的高度比当前的高。

但是这样还不够,假设当前建筑物A,栈顶是B,B下面是C。如果站在A的顶端,A能够看到C,即不会被B挡住,B也不会在挡住后面的视线。

原因是,我们可以从B顶端连接A一直到地面交点D,显然在D以及D左边的,看不到B,会被A挡住,而在D右边的,此时虽然能看到B,但是后面的C更能挡住它,如下图所示:



所以B不会挡住后面,可以把B删掉。

而如果遇到查询的,也是要看下当前栈顶是不是最优。还是那上图,经过我们上面的维护,现在栈顶是A,A下面是C。

如果遇到查询是只能看到A不能看到C,那么就由A算即可。

而如果能看到C,应该把A删去,因为此时A也不可能挡住更往后的查询。删完之后如果栈内还有多个元素,比如C下面还有E,还要继续比较。

左边求完了,右边也类似。

复杂度的话,每个元素都是进入栈两次,出来两次,因为是左右各一次,每次查询取栈顶,复杂度O(N)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 200010;
const double PI = acos(-1.0);
double ans
, X
, H
;
struct Point{
double x, h;
int id;
bool operator < (const Point &A)const{
return x<A.x;
}
}p
;
int T, n, q;
void cal(int st, int ed, int d){
int top=0;
for(int i=st; i!=ed; i+=d){
if(p[i].id){
while(top>1){
double k1 = H[top-1]/fabs(p[i].x-X[top-1]);
double k2 = H[top-2]/fabs(p[i].x-X[top-2]);
if(k1<=k2)  top--;
else    break;
}
ans[p[i].id] += atan(H[top-1]/fabs(p[i].x-X[top-1]));
}
else{
X[top] = p[i].x;
H[top] = p[i].h;
while(top && H[top]>=H[top-1]){
top--;
H[top]=H[top+1];
X[top]=X[top+1];
}
while(top>1){
double k1 = (H[top-1]-H[top])/fabs(X[top-1]-X[top]);
double k2 = (H[top-2]-H[top])/fabs(X[top-2]-X[top]);
if(k1<=k2){
top--;
H[top] = H[top+1];
X[top] = X[top+1];
}
else break;
}
top++;
}
}
}
int main(){
scanf("%d", &T);
for(int t=1; t<=T; t++){
scanf("%d", &n);
for(int i=0; i<n; i++){
scanf("%lf %lf", &p[i].x, &p[i].h);
p[i].id = 0;
}
scanf("%d", &q);
for(int i=0; i<q; i++){
scanf("%lf", &p
.x);
p
.h = 0.0;
p
.id = i+1;
ans[i+1]=0.0;
n++;
}
sort(p, p+n);
cal(0, n, 1);
cal(n-1, -1, -1);
printf("Case #%d:\n", t);
for(int i=1; i<=q; i++) printf("%.10f\n", (PI-ans[i])*180.0/PI);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: