hdu5033 Building单调栈

题意
一条直线上有很多房子,房子的信息包括位置和高度,给出一些询问,当有一个人站在某两个房子之间时,能看到的最大的角度(尽可能广阔的天空)
求解
可以很容易想到求解左边最大的仰角,右边最大的仰角,然后用180度减去两者,所以我们对查询排序,离线依次处理从左到右每个查询的向左看最大仰角,从右向左处理每个查询的向右看最大仰角,离线处理的目的是利用已经处理过的查询的信息,从而降低复杂度,首先,对于一个查询,显然最大仰角只会出现在高度递减的楼中的某一个,其次,一旦求出对应某个最大仰角的楼,其他比这栋楼低的楼不可能在下一次查询的解空间里,因此我们只需要单调栈维护一下递减的楼序列,找出最大值后去掉一些对下次查询不可能的值即可

#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 100050;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
struct Building {
    int pos;
    double height;
    bool operator < (const Building &t)const {
        return pos < t.pos;
    }
};
struct Person {
    int pos,id;
    double Ltan,Rtan;
    bool operator < (const Person &t)const {
        return pos < t.pos;
    }
};
Building building[maxn];
Person person[maxn];
int N,Q,mystack[maxn];
double ans[maxn];

double calcu(int i,int j)//第i个人到第j个房子的tan值
{
    return building[j].height/abs(building[j].pos - person[i].pos);
}

int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i=0;i<N;i++) {
            scanf("%d%lf",&building[i].pos,&building[i].height);
        }
        sort(building,building+N);
        scanf("%d",&Q);
        for(int i=0;i<Q;i++) {
            scanf("%d",&person[i].pos);
            person[i].id = i;
            person[i].Ltan = person[i].Rtan = -1;
        }
        sort(person,person+Q);
        int left = 0;
        int cur = 0;
        for(int i=0;i<Q;i++) //从左到右处理
        {
            for(;left < N && building[left].pos < person[i].pos ; left++)//单调栈维护一下
            {
                while(cur > 0 && building[left].height >= building[mystack[cur-1]].height)
                    cur--;
                mystack[cur++] = left;
            }
            int maxid = -1,k=0;
            for(;k<cur;k++)
            {
                double x = calcu(i,mystack[k]);
                if(x>person[i].Ltan)
                {
                    person[i].Ltan = x;
                    maxid = k;
                }
            }
            cur = maxid + 1; //比当前楼低的楼不可能出现在以后的楼的解空间里
        }
        int right = N-1;
        cur = 0;
        for(int i=Q-1;i>=0;i--) // 从右到左处理,单调栈维护
        {
            for(;right >=0 && building[right].pos > person[i].pos ; right--)
            {
                while(cur > 0 && building[right].height >= building[mystack[cur-1]].height)
                    cur--;
                mystack[cur++] = right;
            }
            int maxid = -1,k=0;
            for(;k<cur;k++)
            {
                double x = calcu(i,mystack[k]);
                if(x>person[i].Rtan)
                {
                    person[i].Rtan = x;
                    maxid = k;
                }
            }
            cur = maxid + 1;
        }
        memset(ans,0,sizeof(ans));
        for(int i=0;i<Q;i++) //求解弧度
        {
            ans[person[i].id] =  PI - atan(person[i].Ltan)- atan(person[i].Rtan);
        }
        printf("Case #%d:\n",cas++);
        for(int i=0;i<Q;i++)
        {
            printf("%lf\n",ans[i]*180.0/PI);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值