题目描述
致力于建设全国示范和谐小村庄的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仅包含一个实数,为塔的最小高度,精确到小数点后三位。
输入输出样例
输入 #1
6
1 2 4 5 6 7
1 2 2 4 2 1
输出 #1
1.000
说明/提示
对于60%的数据, N ≤ 60;
对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。
题解
先做一遍半平面交,由于必定是无界的,我在左右和上侧各加了一个半平面(做完之后把不需要的边界去掉)。
这样我们就得到了一个下凸壳,如果瞭望塔建在x0这个位置,那么必定是建到直线x=x与这个凸壳的交点的高度即可。然后考虑到高度是由上下共同决定的,经过显而易见的贪心,
可以发现瞭望塔的横坐标要么是这个凸壳上的顶点,要么是下方轮廓线的端点,O(n)扫一遍即可。总复杂度O(nlogn)。
#include<bits/stdc++.h>//半平面交 #define ll long long using namespace std; const double M = 1e15; const int N = 1010; struct P { double x, y; } s[N], p[N]; int cnt = 0; struct L { P a, b; double val; } t[N], q[N]; 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; } double ans = 1e15; bool operator<(L a, L b) { if (a.val == b.val)return (a.b - a.a) * (b.b - a.a) > 0; return a.val < b.val; } 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; } double dist(L a, P b) { double c = (a.a.y - a.b.y) / (a.a.x - a.b.x); double d = c * (b.x - a.a.x) + a.a.y; return fabs(b.y - d); } bool check(L a, L b, L c) { P p = inter(a, b); return (c.b - c.a) * (p - c.a) < 0; } void fun() { sort(t + 1, t + 1 + cnt); int l = 1, r = 0; int tot = 0; for (int i = 1; i <= cnt; i++) { if (t[i].val != t[i - 1].val)tot++; t[tot] = t[i]; } cnt = tot; q[++r] = t[1], q[++r] = t[2]; for (int i = 3; i <= cnt; i++) { while (l < r && check(q[r - 1], q[r], t[i]))r--; while (l < r && check(q[l + 1], q[l], t[i]))l++; q[++r] = t[i]; } while (l < r && check(q[r - 1], q[r], q[l]))r--; while (l < r && check(q[l + 1], q[l], q[r]))l++; q[r + 1] = q[l]; cnt = 0; for (int i = l; i <= r; i++) { P o = inter(q[i], q[i + 1]); p[++cnt] = o; } } bool cmp(P a, P b) { if (a.x == b.x) return a.y < b.y; return a.x < b.x; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n; cin >> n; for (int i = 1; i <= n; i++)cin >> s[i].x; for (int i = 1; i <= n; i++)cin >> s[i].y; t[++cnt].a = {s[1].x, M}, t[cnt].b = {s[1].x, 0}; t[cnt].val = atan2(-M, 0); t[++cnt].a = {s[n].x, 0}, t[cnt].b = {s[n].x, M}; t[cnt].val = atan2(M, 0); t[++cnt].a = {M, M}, t[cnt].b = {0, M}; t[cnt].val = atan2(0, -M); for (int i = 1; i < n; i++) { t[++cnt].a = s[i], t[cnt].b = s[i + 1]; t[cnt].val = atan2(s[i + 1].y - s[i].y, s[i + 1].x - s[i].x); } fun(); sort(p + 1, p + 1 + cnt, cmp); int tot = 0; for (int i = 1; i <= cnt; i++) { if (p[i].x == p[tot].x)continue; p[++tot] = p[i]; } cnt = tot; int i = 1, j = 1; p[0].x = p[1].x - 1; p[0].y = p[1].y;//避免第一个不相等的情况 while (i <= cnt && j <= n) { if (p[i].x == s[j].x) { ans = min(ans, p[i].y - s[j].y); i++, j++; } else if (p[i].x < s[j].x) { L c; c.a = s[j], c.b = s[j - 1]; ans = min(ans, dist(c, p[i])); i++; } else if (p[i].x > s[j].x) { L c; c.a = p[i], c.b = p[i - 1]; ans = min(ans, dist(c, s[j])); j++; } } cout << fixed << setprecision(3) << ans << endl; return 0; }