[半平面交] BZOJ1038: [ZJOI2008]瞭望塔

xiaoxiao2021-02-28  111

题意

致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔。 我们将H村抽象为一维的轮廓。 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述,x1 < x2 < …< xn。 瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。 请你写一个程序,帮助dadzhi村长计算塔的最小高度。

题解

刚开始看了半天还没看懂样例,后来才知道,我以为塔是建在x=0上的,其实是在轮廓折线上……我好智障…….. 先不管塔高,考虑要看到某一线段,塔顶必须是在线段所在直线的上方。 显然求一下半平面交即可得到一个下凸的凸壳,在他上方的区域即是塔顶合法位置。 那么如何求塔高呢? 其实我们只需要每枚举在所有折点位置造塔即可,这里说的折点包括轮廓折线上的折点和凸壳上的折点。 这样一定包含最优高度,道理太显然了。

代码借鉴了Claris巨神。自己写的可能是由于精度问题一直wa。

#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define eps 1e-7 using namespace std; const int maxn=305; struct Point{ double x,y; } p[maxn],t; struct Line{ double k,b; void init(Point A,Point B) { k=(A.y-B.y)/(A.x-B.x); b=B.y-k*B.x; } } L[maxn],stk[maxn]; int n,top; double res=1e+50; bool my_cmp(Line A,Line B){ if(abs(A.k-B.k)<eps) return A.b<B.b; return A.k<B.k; } Point inter(Line A,Line B){ Point res; res.x=(B.b-A.b)/(A.k-B.k); res.y=A.k*res.x+A.b; return res; } double f(double x){ double res=0; for(int i=1;i<=top;i++) res=max(res,stk[i].k*x+stk[i].b); return res; } double g(double x){ int i; for(i=n;i&&x<p[i].x;i--); return p[i].y+(x-p[i].x)/(p[i+1].x-p[i].x)*(p[i+1].y-p[i].y); } int main() { freopen("bzoj1038.in","r",stdin); freopen("bzoj1038.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf",&p[i].x); for(int i=1;i<=n;i++) scanf("%lf",&p[i].y); for(int i=1;i<=n-1;i++) L[i].init(p[i],p[i+1]); sort(L+1,L+n,my_cmp); for(int i=1;i<=n-1;i++) if(i==n-1||abs(L[i].k-L[i+1].k)>eps){ while(top>=2&&inter(stk[top],L[i]).x<inter(stk[top-1],stk[top]).x) top--; stk[++top]=L[i]; } for(int i=1;i<=n;i++) res=min(res,f(p[i].x)-p[i].y); for(int i=1;i<=top-1;i++) { Point now=inter(stk[i],stk[i+1]); res=min(res,now.y-g(now.x)); } printf("%.3lf\n",res); return 0; }
转载请注明原文地址: https://www.6miu.com/read-33076.html

最新回复(0)