「BZOJ1038」[ZJOI2008] 瞭望塔
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
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
1.000
「输出样例二」
14.500
HINT
对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。
题解
这个嘛。。。
半平面交求出来之后最后答案一定在山顶上或者求出来凸壳的顶点上
分段一次函数的极值只能在边界或分段点取到。。。——wyl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> #define inf 1000000000 #define ll long long using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } double ans=1e60; int n,cnt,top,tot; struct P{double x,y;}p[1005],a[1005]; struct L{P a,b;double slop;}l[1005],q[1005]; P operator-(P a,P b) { P t;t.x=a.x-b.x;t.y=a.y-b.y;return t; } double operator*(P a,P b) { return a.x*b.y-a.y*b.x; } inline bool operator<(L a,L b) { if(a.slop!=b.slop)return a.slop<b.slop; else return (a.b-a.a)*(b.b-a.a)>0; } P inter(L a,L b) { double k1,k2,t; k1=(b.b-a.a)*(a.b-a.a); k2=(a.b-a.a)*(b.a-a.a); t=k1/(k1+k2); P ans; ans.x=b.b.x+(b.a.x-b.b.x)*t; ans.y=b.b.y+(b.a.y-b.b.y)*t; return ans; } bool jud(L a,L b,L t) { P p=inter(a,b); return (t.b-t.a)*(p-t.a)<0; } void hpi() { int L=1,R=0;tot=0; for(int i=1;i<=cnt;i++) { if(l[i].slop!=l[i-1].slop)tot++; l[tot]=l[i]; } cnt=tot; q[++R]=l[1];q[++R]=l[2]; for(int i=3;i<=cnt;i++) { while(L<R&&jud(q[R-1],q[R],l[i]))R--; while(L<R&&jud(q[L+1],q[L],l[i]))L++; q[++R]=l[i]; } while(L<R&&jud(q[R-1],q[R],q[L]))R--; while(L<R&&jud(q[L+1],q[L],q[R]))L++; tot=0; for(int i=L;i<R;i++) a[++tot]=inter(q[i],q[i+1]); } void pre() { p[0].x=p[1].x;p[0].y=100001; p[n+1].x=p[n].x;p[n+1].y=100001; for(int i=1;i<=n;i++) { l[++cnt].a=p[i-1];l[cnt].b=p[i]; l[++cnt].a=p[i];l[cnt].b=p[i+1]; } for(int i=1;i<=cnt;i++) { l[i].slop=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x); } sort(l+1,l+cnt+1); } void getans() { for(int k=1;k<=tot;k++) for(int i=1;i<n;i++) { P t;t.x=a[k].x;t.y=-1; if(a[k].x>=p[i].x&&a[k].x<=p[i+1].x) ans=min(ans,a[k].y-inter((L){p[i],p[i+1]},(L){t,a[k]}).y); } for(int k=1;k<=n;k++) for(int i=1;i<tot;i++) { P t;t.x=p[k].x;t.y=-1; if(p[k].x>=a[i].x&&p[k].x<=a[i+1].x) ans=min(ans,inter((L){a[i],a[i+1]},(L){t,p[k]}).y-p[k].y); } } int main() { n=read(); for(int i=1;i<=n;i++) p[i].x=read(); for(int i=1;i<=n;i++) p[i].y=read(); pre(); hpi(); getans(); printf("%.3lf",ans); return 0; } |
黄学长,请问p的那个0和n+1的预处理原因是什么啊?
[…] 正解是模拟退火或半平面交。 […]
黄学长,请问一下为啥第76行那个循环,除了边界一条边要加进去两次呢?
黄学长…这道题啥意思啊…没看懂….
在hpi里面为什么要维护一个L呢?我举不出L会变的例子…
QAQ 半平面交不就这么写么
我觉得这道题里的L不会变呀…
这道题我就不知道啦我直接撸模板
= = 好吧