【BZOJ】【1038】【ZJOI2008】瞭望塔

计算几何/半平面交


  说是半平面交,实际上只是维护了个下凸壳而已……同1007水平可见直线

  对于每条线段,能看到这条线段的点都在这条线段的“上方”,那么对所有n-1条线段求一个可视区域的交,就是求一个半平面交……(好扯)

  一开始我想的是:直接找到这个下凸壳的最低点,它的y值就是答案辣~但是明显不对>_>这题让求的是塔的最低高度……不光要考虑塔顶,还要看塔底的啊!

  那么我们怎么找呢?我们可以发现:随着x的变化,塔高(就是地面到凸壳的竖直距离,y坐标之差)是一个分段函数,分段点就是地面的折点以及凸壳的顶点!而且在每一段里面,塔高的值是一个一次函数!经过大胆猜想,小(bu)心(yong)证明我们发现:分段一次函数的极值在分段点和边界点处取到。

  那么就是对这些点算一下答案就可以了……点数是O(n)的……

 1 /**************************************************************
 2     Problem: 1038
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:0 ms
 7     Memory:1292 kb
 8 ****************************************************************/
 9  
10 //BZOJ 1038
11 #include<cmath>
12 #include<vector>
13 #include<cstdio>
14 #include<cstring>
15 #include<cstdlib>
16 #include<iostream>
17 #include<algorithm>
18 #define rep(i,n) for(int i=0;i<n;++i)
19 #define F(i,j,n) for(int i=j;i<=n;++i)
20 #define D(i,j,n) for(int i=j;i>=n;--i)
21 #define pb push_back
22 using namespace std;
23 inline int getint(){
24     int v=0,sign=1; char ch=getchar();
25     while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
26     while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
27     return v*sign;
28 }
29 const int N=310,INF=~0u>>2;
30 typedef long long LL;
31 typedef double lf;
32 const lf eps=1e-8;
33 /******************tamplate*********************/
34 struct Point{
35     lf x,y;
36     void read(){scanf("%lf%lf",&x,&y);}
37 }p[N];
38 struct Line{double k,b;}l[N],st[N];
39 Line make_line(Point a,Point b){
40     Line tmp;
41     tmp.k=(a.y-b.y)/(a.x-b.x);
42     tmp.b=a.y-tmp.k*a.x;
43     return tmp;
44 }
45 int n,top;
46 inline bool cmp(Line a,Line b){
47     if (fabs(a.k-b.k)<eps) return a.b<b.b;
48     return a.k<b.k;
49 }
50 double crossx(Line x1,Line x2){
51     return (x2.b-x1.b)/(x1.k-x2.k);
52 }
53 void insert(Line a){
54     while(top){
55         if (fabs(st[top].k-a.k)<eps) top--;
56         else if (top>1 && crossx(a,st[top-1])<=
57                           crossx(st[top],st[top-1])) top--;
58         else break;
59     }
60     st[++top]=a;
61 }
62 lf Up(lf x){
63     lf ans=0.0;
64     F(i,1,top)
65         ans=max(ans,st[i].k*x+st[i].b);
66     return ans;
67 }
68 lf Down(lf x){
69     int pos;
70     for(pos=1;pos<n && p[pos+1].x<x;pos++);
71     if (pos==n) return -1e10;
72     Line tmp=make_line(p[pos],p[pos+1]);
73     return tmp.k*x+tmp.b;
74 }
75  
76 int main(){
77 #ifndef ONLINE_JUDGE
78     freopen("1038.in","r",stdin);
79     freopen("1038.out","w",stdout);
80 #endif
81     n=getint();
82     F(i,1,n) scanf("%lf",&p[i].x);
83     F(i,1,n) scanf("%lf",&p[i].y);
84     F(i,1,n-1) l[i]=make_line(p[i+1],p[i]);
85     sort(l+1,l+n,cmp);
86     F(i,1,n-1) insert(l[i]);
87     lf ans=1e10;
88     F(i,1,n) ans=min(ans,Up(p[i].x)-p[i].y);
89     F(i,2,top){
90         Point p;
91         p.x=crossx(st[i-1],st[i]);
92         p.y=st[i].k*p.x+st[i].b;
93         ans=min(ans,p.y-Down(p.x));
94     }
95     printf("%.3lf\n",ans);
96     return 0;
97 }
View Code

 

1038: [ZJOI2008]瞭望塔

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1043  Solved: 470
[Submit][Status][Discuss]

Description

致 力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长希望建造的塔 高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。

Input

第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。

Output

仅包含一个实数,为塔的最小高度,精确到小数点后三位。

Sample Input

【输入样例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【输入样例二】
4
10 20 49 59
0 10 10 0

Sample Output

【输出样例一】
1.000
【输出样例二】
14.500

HINT

对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。

Source

[Submit][Status][Discuss]
posted @ 2015-04-14 21:53  Tunix  阅读(461)  评论(0编辑  收藏  举报