http://acm.hdu.edu.cn/showproblem.php?pid=5033
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
Case #1: 101.3099324740 Case #2: 90.0000000000 Case #3: 78.6900675260
分析:可以用单调栈来维护;
具体如下:
第一种单调递减情况是
栈中不需要维护中间两点,这个线段对于之后的观察员来说只有首尾两点的高度来决定;
第二种单调递减情况是
栈中需要维护这四个顶点因为从左向右走到足够远的地方,每个点的高度都有可能成为限制点;
例如下图:
用q[]数组模拟栈,p[]数组记录按照x坐标排序的点A,B……;首先把A,A入栈,然后判断AAB是符合第几种单调情况;经判断把B入栈,然后判断ABC是符合第二种情况把C入栈,BCD符合第一种情况,把C出栈,发现此时ABD符合第二种,把D入栈,此时D的点是观察员的位置,所以他的左边应该是由B点的高度决定,然后判断BDE符合第一种,把D出栈,此时ABE符合第二种,把E入栈;然后BEF符合第一种把E出栈,ABF符合第二种把F入栈,接着BFG符合第二种把G入栈,G是观察员的位置,左边由F点的高度决定,一次类推。。。
对于右面的点,只需要按照上述原理从右向左判断一遍即可;
程序:
#include"stdio.h" #include"string.h" #include"stack" #include"stdlib.h" #include"iostream" #include"algorithm" #include"math.h" #define M 200009 #define inf 100000000 #define eps 1e-10 #define PI acos(-1.0) using namespace std; struct node { double x,y; int id; }p[M],q[M],ans[M]; int cmp(node a,node b) { return a.x<b.x; } double cross(node a,node b,node c) { double px1=b.x-a.x; double px2=c.x-a.x; double py1=b.y-a.y; double py2=c.y-a.y; return px1*py2-px2*py1; } int main() { int T,n,i,m,kk=1; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); p[i].id=i; } scanf("%d",&m); for(i=n;i<m+n;i++) { scanf("%lf",&p[i].x); p[i].y=0; p[i].id=i; } sort(p,p+n+m,cmp); q[0]=p[0]; q[1]=p[0]; int cnt=1; for(i=1;i<m+n;i++) { while(cross(q[cnt-1],q[cnt],p[i])>-eps&&cnt-1>=0) cnt--; q[++cnt]=p[i]; if(p[i].y<eps) { ans[p[i].id].x=fabs(q[cnt-1].y/(q[cnt-1].x-q[cnt].x)); } } q[0]=q[1]=p[m+n-1]; cnt=1; for(i=m+n-2;i>=0;i--) { while(cross(q[cnt-1],q[cnt],p[i])<eps&&cnt>=1) cnt--; q[++cnt]=p[i]; if(p[i].y<eps) { ans[p[i].id].y=fabs(q[cnt-1].y/(q[cnt-1].x-q[cnt].x)); } } printf("Case #%d:\n",kk++); for(i=n;i<m+n;i++) printf("%.10lf\n",(PI-atan(ans[i].x)-atan(ans[i].y))/PI*180); } }