[ZJOI2008]瞭望塔
题目描述
致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。
我们将H村抽象为一维的轮廓。如下图所示
我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长希望建造的塔高度尽可能小。
请你写一个程序,帮助dadzhi村长计算塔的最小高度。
输入输出格式
输入格式:
输入文件tower.in第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。
输出格式:
输出文件tower.out仅包含一个实数,为塔的最小高度,精确到小数点后三位。
输入输出样例
说明
对于60%的数据, N ≤ 60;
对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题
这题分两步。第一步就是求出满足条件的半平面,这半部分就是"水平可见直线"那题。
第二部就是计算答案。可以证明瞭望塔的横坐标一定在半平面或地面的拐点处,因为中间部分一定没有其中一端优秀。
所以对半平面和地面的拐点分别拎出来讨论一下就好了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct Line 8 { 9 double k,b; 10 }line[10001],sta[10001]; 11 int n,top; 12 double x[1002],y[1001],inf=1e8,eps=1e-10,ans=2e12; 13 int dcmp(double X) 14 { 15 if (X>eps) return 1; 16 if (X<-eps) return -1; 17 return 0; 18 } 19 double getx(Line a,Line b) 20 { 21 return ((b.b-a.b)/(a.k-b.k)); 22 } 23 double gety(double X) 24 {int i; 25 for (i=1;i<=n;i++) 26 { 27 if (x[i+1]>=X) break; 28 } 29 if (i==n+1) return -inf; 30 double k=(y[i+1]-y[i])/(x[i+1]-x[i]),b=y[i]-k*x[i]; 31 return X*k+b; 32 } 33 bool cmp(Line a,Line b) 34 { 35 if (dcmp(a.k-b.k)==0) 36 return a.b<b.b; 37 return a.k<b.k; 38 } 39 int main() 40 { 41 int i,j; 42 double maxx; 43 cin>>n; 44 for (i=1;i<=n;i++) 45 scanf("%lf",&x[i]); 46 for (i=1;i<=n;i++) 47 scanf("%lf",&y[i]); 48 for (i=1;i<=n-1;i++) 49 { 50 line[i].k=(y[i+1]-y[i])/(x[i+1]-x[i]); 51 line[i].b=y[i]-line[i].k*x[i]; 52 } 53 n--; 54 sort(line+1,line+n+1,cmp); 55 line[n+1].k=inf; 56 sta[1]=line[1];sta[2]=line[2]; 57 top=2; 58 for (i=3;i<=n;i++) 59 { 60 if (dcmp(line[i].k-line[i+1].k)==0) 61 continue; 62 while (top>1&&getx(line[i],sta[top-1])<=getx(sta[top],sta[top-1])) top--; 63 top++; 64 sta[top]=line[i]; 65 } 66 for (i=1;i<top;i++) 67 { 68 double X=getx(sta[i],sta[i+1]); 69 double Y=sta[i].k*X+sta[i].b; 70 if (dcmp(Y-gety(X))>=0) 71 ans=min(ans,Y-gety(X)); 72 } 73 for (i=1;i<=n+1;i++) 74 { 75 maxx=0; 76 for (j=1;j<=top;j++) 77 { 78 maxx=max(maxx,x[i]*sta[j].k+sta[j].b); 79 } 80 if (dcmp(maxx-y[i])>=0) 81 ans=min(ans,maxx-y[i]); 82 } 83 printf("%.3lf\n",ans); 84 }
本题可以使用三分法
将点按横坐标排好序后
对于任意相意两个点连成的线段,瞭望塔的高度 是单峰函数,而且是下凸函数
感性理解单峰就是
瞭望塔建的靠左,为了能看到右边的,要高一点
瞭望塔建的靠右,为了能看到左边的,要高一点
所以 枚举所有线段,三分线段上建造瞭望塔的位置,所有线段上的瞭望塔高度取最小
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct Node 8 { 9 double k,b; 10 int pd; 11 }L[1001]; 12 int n; 13 double px[1001],py[1001],eps=1e-8,ans; 14 int dcmp(double x) 15 { 16 if (x>eps) return 1; 17 if (x<-eps) return -1; 18 return 0; 19 } 20 double cal(double X,double Y) 21 {int i; 22 double tmp=0; 23 for (i=1;i<n;i++) 24 { 25 if (L[i].pd==0) continue; 26 tmp=max(tmp,L[i].k*X+L[i].b-Y); 27 } 28 return tmp; 29 } 30 int main() 31 {int i; 32 cin>>n; 33 for (i=1;i<=n;i++) 34 scanf("%lf",&px[i]); 35 for (i=1;i<=n;i++) 36 scanf("%lf",&py[i]); 37 for (i=1;i<n;i++) 38 { 39 if (dcmp(px[i]-px[i+1])==0) continue; 40 L[i].k=(py[i]-py[i+1])/(px[i]-px[i+1]); 41 L[i].b=py[i]-L[i].k*px[i]; 42 L[i].pd=1; 43 } 44 ans=2e15; 45 for (i=1;i<n;i++) 46 { 47 if (L[i].pd==0) continue; 48 int T=100; 49 double l=px[i],r=px[i+1]; 50 double mid1,mid2; 51 while (T--) 52 { 53 mid1=(r-l)/3+l,mid2=r-(r-l)/3; 54 if (dcmp(cal(mid1,mid1*L[i].k+L[i].b)-cal(mid2,mid2*L[i].k+L[i].b))>=0) l=mid1; 55 else r=mid2; 56 } 57 ans=min(ans,cal(mid1,mid1*L[i].k+L[i].b)); 58 } 59 printf("%.3lf\n",ans); 60 }