致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长希望建造的塔高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。
第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。
仅包含一个实数,为塔的最小高度,精确到小数点后三位。
【输入样例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【输入样例二】
4
10 20 49 59
0 10 10 0
【输出样例一】
1.000
【输出样例二】
14.500
对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。
首先我们可以发现如果我们把每两个点之间连线,作为一个形如 ax+by+c≤0 或者大于等于等关系的式子我们求这样的式子的交集叫做求半平面交,那么我们可以发现在本题中,答案一定出现在半平面交的集合中,对于每两个点无论是村庄的轮廓点还是交集的边界点,我们可以发现如果做垂直于 x 轴的直线我们可以划分成(令半平面交集的转折点数量为 m 下面的村庄转折点数量为 m )那么我们可以将整个平面划分成 n+m+1 块,在每一块中间都是由上下两个直线构成的,我们要求的答案就是每一块中两个直线距离最小的最小值,那么在每一块中因为上下都是支线,那么一定满足单调性,我们检查每一块的左右端点取较小值就行了。
这里提供一个求半平面交集的方法(仅限本题目, 可以想一下为什么):http://blog.csdn.net/jeremygjy/article/details/50623877
对于刚才的说明,我画了一张图,求得就是每两条绿色支线中黑色线段和红色线段距离的最小值
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#include <cmath>
#define mcp(a,b) fabs((a)-(b))<eps
using namespace std;
const int MAXN = 300;
const double eps = 1e-8;
const int INF = 1000000000;
struct Point{
double x, y;
Point(){x=y=0;}
bool operator<(const Point& pt) const{
return x < pt.x;
}
bool operator>(Point pt) const{
return x > pt.x;
}
}List[MAXN+10], List2[MAXN+10];
struct Line{
double a, b;
Point GetD(const Line& c){
Point ret;
ret.x = (1.0 * (c.b - b)) / (a - c.a);
ret.y = a * ret.x + b;
return ret;
}
void Make(Point _a, Point _b){
a = (_a.y - _b.y) / (_a.x - _b.x);
b = _a.y - a * _a.x;
}
bool operator == (const Line& c) {
return c.a + eps >= a && c.a - eps <= a;
}
}T[MAXN+10];
stack<Line> ans;
stack<Point> jd;
bool cmp(Line a, Line b){
if(mcp(a.a, b.a))
return a.b < b.b;
return a.a < b.a;
}
bool cmp3(Point a, Point b){
return a.x < b.x;
}
int pcmp(Point p, Line l){
double y = l.a * p.x + l.b;
if(y >= p.y-eps)
return -1;
return 1;
}
void solve(int n){
int tn = 0;
sort(T+1, T+1+n, cmp);
for(int i=1;i<=n;i++){
while(mcp(T[i].a,T[i+1].a)) i++;
T[++tn] = T[i];
}
ans.push(T[1]);
for(int i=2;i<=tn;i++){
while(!jd.empty()){
Point tp = jd.top();
if(pcmp(tp, T[i]) <= 0){
jd.pop();
ans.pop();
}else break;
}
jd.push(T[i].GetD(ans.top()));
ans.push(T[i]);
}
}
int main(){
Line tmp;
int n;
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%lf", &List[i].x);
for(int i=1;i<=n;i++) scanf("%lf", &List[i].y);
for(int i=2;i<=n;i++)
T[i-1].Make(List[i-1], List[i]);
solve(n-1);
int cnt = 0;
while(!jd.empty()){
List2[++cnt] = jd.top();
jd.pop();
}
sort(List2+1, List2+1+cnt, cmp3);
double answer = 1e20;
for(int i=1;i<n;i++){
tmp.Make(List[i], List[i+1]);
int pos = lower_bound(List2+1, List2+1+cnt, List[i]) - List2;
for(int j=pos;List2[j].x <= List[i+1].x+eps && j <= cnt;j++)
answer = min(answer, max(0.0 , List2[j].y - List2[j].x*tmp.a - tmp.b));
}
int ct2 = ans.size();
int ctmp = ct2;
while(!ans.empty()){
T[--ct2] = ans.top();
ans.pop();
}
ct2 = ctmp;
int tp = lower_bound(List+1, List+1+n, List2[1]) - List;
if(List[tp].x >= List2[1].x-eps) tp--;
while(tp){
answer = min(answer, max(0.0, T[0].a*List[tp].x+T[0].b-List[tp].y));
tp--;
}
tp = lower_bound(List+1, List+1+n, List2[cnt]) - List;
while(tp <= n){
answer = min(answer, max(0.0, T[cnt].a*List[tp].x+T[cnt].b-List[tp].y));
tp++;
}
for(int i=1;i<cnt;i++){
tp = lower_bound(List+1, List+1+n, List2[i]) - List;
for(int j=tp;List[j].x <= List2[i+1].x && j <= n;j++)
answer = min(answer, max(0.0, T[i].a*List[j].x+T[i].b-List[j].y));
}
printf("%.3lf\n", answer);
return 0;
}