hdu5033(单调栈)

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:\n",kase);
        for(int i=1;i<=q;i++){
            printf("%.10lf\n",ans[i]*180/Pi);
        }
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/shutdown113/p/9381140.html