题目大意:
就是一个人来到充满摩天大楼的城市,所有大楼没有宽度。建一直角坐标系,给出每个建筑的高度,现在求人站在(x,0)处能够看到天空的范围。(即不被摩天大楼阻挡)。答案只需要给出视角大小。
还是想了一会才想出。.对于一个人能看到的左界,我们可以得到下式:
kmaxxi=min((Hj−Hi)/(Xj−Xi)),j<=i
k
m
a
x
x
i
=
m
i
n
(
(
H
j
−
H
i
)
/
(
X
j
−
X
i
)
)
,
j
<=
i
用求出左界的斜率即可反解该范围的视角的角。而求最小即用单调栈维护斜率单减即可。右界同理可得。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000100,inf=1e9;
const double pi=3.1415926535;
int t,n,cnt;
stack <pair<int,double> > s;//第一个是当前元素编号,第二个是与上一个元素的斜率。
struct node{
int x,h,bian;
double l,r;
}a[maxn];
bool cmp(node p,node q){
return p.x<q.x;
}
bool cmp2(node p,node q){
return p.bian<q.bian;
}
double getk(int x,int y){
double x1=a[x].x,x2=a[y].x,y1=a[x].h,y2=a[y].h;
return (y2-y1)/(x2-x1);
}
int main(){
int i,j,q;double k,a1,a2;
scanf("%d",&t);
for(j=1;j<=t;j++){
scanf("%d",&n);
printf("Case #%d\n",j);
cnt=0;
for(i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].h);
a[i].bian=inf;//大楼的编号为正无穷
}
cnt=n;
scanf("%d",&q);
for(i=1;i<=q;i++){
cnt++;
scanf("%d",&a[cnt].x);
a[cnt].bian=i;
a[cnt].h=0;
}
sort(a+1,a+cnt+1,cmp);//把人和摩天大楼同时排序,不用分类讨论。
while(!s.empty())s.pop();
for(i=1;i<=cnt;i++){//求左界,维护斜率单减
while(!s.empty()){
k=getk(s.top().first,i);
if(k>=s.top().second)s.pop();
else break;
}
if(s.empty()){
a[i].l=0;
s.push(make_pair(i,double(inf)));
}
else {
a[i].l=getk(s.top().first,i);
s.push(make_pair(i,a[i].l));
}
}
while(!s.empty())s.pop();
for(i=cnt;i>=1;i--){//求右界,维护斜率单增
while(!s.empty()){
k=getk(s.top().first,i);
if(k<=s.top().second)s.pop();
else break;
}
if(s.empty()){
a[i].r=0;
s.push(make_pair(i,double(-inf)));
}
else {
a[i].r=getk(s.top().first,i);
s.push(make_pair(i,a[i].r));
}
}
sort(a+1,a+cnt+1,cmp2);
for(i=1;i<=q;i++){
a1=atan(abs(a[i].l));
a2=atan(abs(a[i].r));
printf("%.10lf\n",(pi-a1-a2)/pi*180);
}
}
return 0;
}