hdu5033Building+计算几何+单调栈

Problem Description
Once upon a time Matt went to a small town. The town was so small and narrow that he can regard the town as a pivot. There were some skyscrapers in the town, each located at position xi with its height hi. All skyscrapers located in different place. The skyscrapers had no width, to make it simple. As the skyscrapers were so high, Matt could hardly see the sky.Given the position Matt was at, he wanted to know how large the angle range was where he could see the sky. Assume that Matt’s height is 0. It’s guaranteed that for each query, there is at least one building on both Matt’s left and right, and no building locate at his position.

Input
The first line of the input contains an integer T, denoting the number of testcases. Then T test cases follow.

Each test case begins with a number N(1<=N<=10^5), the number of buildings.

In the following N lines, each line contains two numbers, xi(1<=xi<=10^7) and hi(1<=hi<=10^7).

After that, there’s a number Q(1<=Q<=10^5) for the number of queries.

In the following Q lines, each line contains one number qi, which is the position Matt was at.

Output
For each test case, first output one line “Case #x:”, where x is the case number (starting from 1).

Then for each query, you should output the angle range Matt could see the sky in degrees. The relative error of the answer should be no more than 10^(-4).

Sample Input

3
3
1 2
2 1
5 1
1
4
3
1 3
2 2
5 1
1
4
3
1 4
2 3
5 1
1
4

Sample Output

Case #1:
101.3099324740
Case #2:
90.0000000000
Case #3:
78.6900675260

Source
2014 ACM/ICPC Asia Regional Beijing Online

单调栈与单调队列很相似。首先栈是后进先出的,单调性指的是严格的递增或者递减。

单调栈有以下两个性质:

1、若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严格递减的。

2、越靠近栈顶的元素越后进栈。

单调栈与单调队列不同的地方在于栈只能在栈顶操作,因此一般在应用单调栈的地方不限定它的大小,否则会造成元素无法进栈。

元素进栈过程:对于单调递增栈,若当前进栈元素为e,从栈顶开始遍历元素,把小于e或者等于e的元素弹出栈,直接遇到一个大于e的元素或者栈为空为止,然后再把e压入栈中。对于单调递减栈,则每次弹出的是大于e或者等于e的元素。

一个单调递增栈的例子:

进栈元素分别为3,4,2,6,4,5,2,3

3进栈:(3)

3出栈,4进栈:(4)

2进栈:(4,2)

2出栈,4出栈,6进栈:(6)

4进栈:(6,4)

4出栈,5进栈:(6,5)

2进栈:(6,5,2)

2出栈,3进栈:(6,5,3)

以上左端为栈底,右端为栈顶。

/**********************************************************************/
题意:n个建筑物,Q条询问,问所在的位置,看到天空的角度是多少,每条询问的位置左右必定是有建筑物的。

思路 : 维护一个单调栈,将所有的建筑物和所有的人都放到一起开始算就行,每加入一个人,就维护栈里的建筑物的高度,也就是说这个人所能够看到的建筑物时在栈里的,但是这个人看不到的就删掉,例如下图,中间点可以删掉了,因为角度问题中间三个是看不到的,所以不会影响最终求得角度
这里写图片描述
还有一种情况。
这里写图片描述
维护的是相邻两建筑顶(xi,hi)的连线的斜率的绝对值上升 的单调栈。
此外:右边比左边高也是没必要的。
最后:从左往右一遍,在右往左一遍(翻转点)。

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
#define LL long long
#define pi acos(-1.0)

const int Max=1e5+20;

struct Node{
    double high;
    double x;
    int id;
    Node(){};
    Node(double high,double x,int id):high(high),x(x),id(id){};
    bool operator <(const Node &b)const{
        return x<b.x;
    }
    Node operator -(const Node &b)const{
        return Node(high-b.high,x-b.x,0);
    }
};
Node tp[2*Max];
Node a[2*Max];

double ans[2*Max];

//判断在c处的视野中,a建筑物是否能够在c处没有被b建筑物挡住
int cross(const struct Node &a,const struct Node &b){
    return a.high*b.x-b.high*a.x>0;
}
int judge(Node &a,Node &b,Node &c){
    return cross(a-c,c-b);
}
//求a建筑物与b处人所成角的大小
double angle(Node a,Node b){
    return atan((b.x-a.x)/(a.high)) ;
}
int n;
int all;//包括询问的点数

void solve(){
    int top=0;
    for(int i=0;i<all;i++){
        if(a[i].high==0){//是人的位置
            while(top>=2&&judge(tp[top-1],tp[top],a[i])) top--;//去掉凹处
            ans[a[i].id]+=angle(tp[top],a[i]);
            //cout<<angle(tp[top-1],a[i])<<endl;
        }
        else{
            while(top&&tp[top].high<=a[i].high) top--;//比当前还要矮的不要
            while(top>=2&&judge(tp[top-1],tp[top],a[i])) top--;//去掉凹处
            tp[++top]=a[i];
        }
    }
}
int main(){
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%lf %lf",&a[i].x,&a[i].high);
        }
        all=n;
        int q;
        scanf("%d",&q);
        for(int i=0;i<q;i++){
            scanf("%lf",&a[all].x);
            a[all].high=0;
            a[all].id=i;
            all++;
        }

        sort(a,a+all);
        memset(ans,0,sizeof(ans));
        solve();
        reverse(a,a+all); //翻转,倒回来计算右边的角度
        for(int i=0;i<all;i++) a[i].x=10000000-a[i].x ; //最大的x,变最小
        solve();

        printf("Case #%d:\n",cas);
        for(int i=0;i<q;i++){
            printf("%.10f\n",ans[i]*180/pi);//角度与弧度的转化
        }
    }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值