Building
题意:
x轴上有n栋高为hi的建筑,现在人在x轴上任意一个位置(保证左右一定有建筑,人的位置没有建筑,人的高可以视为0),求人所能看见的最大的天空的角度。如图所示:
分析:
整体思路就是维护一个相邻建筑顶连线斜率绝对值上升的单调栈,把建筑和人放在一起计算。首先我们考虑,什么情况下,建筑物会被其他的建筑物遮盖。
1.a建筑比b建筑矮(a在b的左边),当人从b的右边看的时候,肯定是看不到a的,同理,a比b高,人从a左边也看不到b。
2.如果a,b,c(从左到右)的高度如果成一个凹字型,那么b也是看不到的。
然后我们就根据这两点来维护单调栈。当遇见人的时候,如果单调栈中建筑与人之间满足上面两点,则单调栈弹出元素,直到找到人左边(右边)所能够看到的最高的建筑,计算出视线与建筑的夹角即可。遇到建筑的时候,则只需要维护单调栈就好了。
学习资料:大佬博客
代码:
#include <bits/stdc++.h> using namespace std; #define Pi acos(-1.0) #define ll long long #define ull unsigned long long #define cls(x) memset(x,0,sizeof(x)) #define clslow(x) memset(x,-1,sizeof(x)) const int maxn=1e5+100; int n,q,top,tot,T; double ans[maxn]; struct Node { int id; double x,h; Node() {} Node(int id,double x,double h):id(id),x(x),h(h) {} bool operator < (const Node& rhs) const { return x<rhs.x; } Node operator - (const Node& rhs) const { return Node(0,abs(x-rhs.x),abs(h-rhs.h)); } }; //栈 Node st[maxn<<1]; Node node[maxn<<1]; bool cross(Node a,Node b) { return b.h*a.x<a.h*b.x; } //如果为凹型,则中间的建筑物是看不到的 bool judge(Node a,Node b,Node c) { return cross(c-a,c-b); } //注意算的是哪个角 double angel(Node a,Node b) { return atan((b.x-a.x)/a.h); } void solve() { top=0; for(int i=1;i<=tot;i++){ //如果是建筑物 if(node[i].id==0){ //去掉比当前矮的 while(top&&st[top].h<=node[i].h) top--; //去掉凹处 while(top>=2&&judge(st[top-1],st[top],node[i])) top--; st[++top]=node[i]; } //如果是人 else{ while(top>=2&&judge(st[top-1],st[top],node[i])) top--; ans[node[i].id]+=angel(st[top],node[i]); } } } int main() { // freopen("in.txt","r",stdin); scanf("%d",&T); for(int kase=1;kase<=T;kase++){ scanf("%d",&n); for(int i=1;i<=n;i++){ node[i].id=0; scanf("%lf%lf",&node[i].x,&node[i].h); } tot=n; scanf("%d",&q); for(int i=1;i<=q;i++){ tot++; node[tot].h=0; node[tot].id=i; scanf("%lf",&node[tot].x); } cls(ans); //计算左边的角度 sort(node+1,node+tot+1); solve(); //翻转,计算右边的角度 reverse(node+1,node+tot+1); //最大的x变最小的x for(int i=1;i<=tot;i++) node[i].x=1e7-node[i].x; solve(); printf("Case #%d: ",kase); for(int i=1;i<=q;i++){ printf("%.10lf ",ans[i]*180/Pi); } } return 0; }