• P2600 [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仅包含一个实数,为塔的最小高度,精确到小数点后三位。

    输入输出样例

    输入 #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;
    }
  • 相关阅读:
    博客园开通新随笔
    遍历两个数组,并输出数组中的不同内容
    1021-二叉树复制和左右子树互换
    1020-层次遍历二叉树
    1019-计算二叉树的高度和结点数
    1018-深度遍历二叉树
    1017-乘积最大
    1016-求幂
    1015-最大公约数和最小公倍数
    1014-数据的插入与删除
  • 原文地址:https://www.cnblogs.com/nublity/p/11650350.html
Copyright © 2020-2023  润新知