第四节 凸包

凸包类

  • conv get_convex(vector<pt> a)
    用点集求出凸包(逆时针保存)
  • bool isconvex(const vector<pt> &a)
    对给定的点集(顺逆时针均可),判断是否构成凸包
  • bool containOn(const conv &a, const pt &b)
    复杂度 O(n) 判断点 b 是否在凸包 a 的内部或边界上
  • bool containOlogn(const conv &a, const pt &b)
    复杂度 O(\log n) 判断点 b 是否在凸包 a 的内部或边界上
  • db convex_dia(conv &a, int &fi, int &se)
    返回凸包的直径,两个点分别记录在 fise
namespace Convex {
    struct conv {
        vector<pt> P;
        conv(int sz = 0){
            P.resize(sz);
        }
        db cir() {
            db res = 0; int n = (int)P.size();
            for(int i = 0; i < n; i++) res += dist(P[i], P[nex(i)]);
            return res;
        }
        db area() {
            db sum = 0; int n = (int)P.size();
            for(int i = 0; i < n; i++) sum += det(P[i], P[nex(i)]);
            return sum / 2.0;
        }
    };

    conv get_convex(vector<pt> a) {
        // 若要凸包上包含共线,则下面while中的小于等于改为小于
        conv res(2 * (int)a.size() + 5);
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
        int m = 0, sz = (int)a.size();;
        for (int i = 0; i < sz; i++) {
            while (m > 1 && cmp(det(res.P[m-1] - res.P[m-2], a[i] - res.P[m-2])) <= 0) m--;
            res.P[m++] = a[i];
        }
        int k = m;
        for(int i = sz - 2; i >= 0; i--) {
            while (m > k && cmp(det(res.P[m-1] - res.P[m-2], a[i] - res.P[m-2])) <= 0) m--;
            res.P[m++] = a[i];
        }
        res.P.resize(m);
        if (sz > 1) res.P.resize(m - 1);
        return res;
    }

    bool isconvex(const vector<pt> &a) {
        int n = (int)a.size();
        int dir = cmp(det(a[1] - a[0], a[2] - a[1])) < 0 ? -1 : 1;
        for (int i = 1; i < n; i++) {
            int now = cmp(det(a[nex(i)] - a[i], a[nex(nex(i))] - a[nex(i)]));
            int tmp = now < 0 ? -1 : 1;
            if(tmp != dir) return false;
        }
        return true;
    }

    bool containOn(const conv &a, const pt &b) {
        int n = (int)a.P.size(), sign = 0;
        for (int i = 0; i < n; i++) {
            int x = cmp(det(a.P[i] - b, a.P[nex(i)] - b));
            if (x) {
                if (sign) {if(sign != x) return false;}
                else sign = x;
            }
        }
        return true;
    }

    int containOlogn(const conv &a, const pt &b) {
        int n = (int)a.P.size();
        pt g = (a.P[0] + a.P[n / 3] + a.P[2 * n / 3]) / 3.0;
        int l = 0, r = n;
        while (l + 1 < r) {
            int mid = (l + r) >> 1;
            int dl = cmp(det(a.P[l] - g, b - g));
            if (cmp(det(a.P[l] - g, a.P[mid] - g)) > 0) {
                if(dl >= 0 &&
                   cmp(det(a.P[mid] - g, b - g)) < 0) r = mid;
                else l = mid;
            }
            else {
                if (cmp(det(a.P[l]-g, b-g)) < 0 &&
                    cmp(det(a.P[mid]-g, b-g)) >= 0) l = mid;
                else r = mid;
            }
        }
        r %= n;
        int z = cmp(det(a.P[r]-b, a.P[l]-b)) - 1;
        if (z == -2) return 1;
        return z;
    }

    db convex_dia(conv &a, int &fi, int &se) {
        vector<pt> &p = a.P;
        db maxd = 0; int n = (int)p.size();
        if (n == 1) {
            fi = se = 0;
            return maxd;
        }
        for (int i = 0, j = 1; i < n; i++) {
            while (cmp(det(p[nex(i)] - p[i], p[j] - p[i]) -
                       det(p[nex(i)] - p[i], p[nex(j)] - p[i])) < 0) j = nex(j);
            db d = dist(p[i], p[j]);
            if (d > maxd) {
                maxd = d;
                fi = i; se = j;
            }
            d = dist(p[nex(i)], p[nex(j)]);
            if (d > maxd) {
                maxd = d;
                fi = i; se = j;
            }
        }
        return maxd;
    }
}
using namespace Convex;

例题:poj1228 Grandpa’s Estate

题目大意:

n 个点的稳定凸包(这几个点确定的凸包是唯一的)。

分析:

即凸包每条边上点的数量大于 2.

bool judge(const conv &a) {
    int n = (int)a.P.size();
    if (n < 6) return false;
    for (int i = 0, flag = 1; i < n; i++) {
        Line::line tmp1(a.P[i], a.P[nex(i)]);
        Line::line tmp2(a.P[nex(i)], a.P[nex(nex(i))]);
        if (!Line::parallel(tmp1, tmp2)) {
            if (!flag) return false;
            else flag = 0;
        }
        else flag = 1;
    }
    return true;
}

int main() {
    int T; scanf("%d", &T);
    while(T--) {
        int n; scanf("%d", &n);
        vector<pt> v; pt tmp;
        for (int i = 1; i <= n; i++) {
            tmp.input();
            v.push_back(tmp);
        }
        a = get_convex(v);
        if (judge(a)) puts("YES");
        else puts("NO");
    }
    return 0;
}

闵可夫斯基和

两个图形 A,B 的闵可夫斯基和 C=A+B=\begin{Bmatrix}a+b|a\in A,b\in B\end{Bmatrix}

conv Minkowski(conv A, conv B) {
    size_t n = A.P.size(), m = B.P.size();
    vector<pt> v(n), w(m), x;
    for (int i = 0; i < n - 1; i++) v[i] = A.P[i + 1] - A.P[i];
    v[n - 1] = A.P[0] - A.P[n - 1];
    for (int i = 0; i < m - 1; i++) w[i] = B.P[i + 1] - B.P[i];
    w[m - 1] = B.P[0] - B.P[m - 1];
    x.push_back(A.P[0] + B.P[0]);
    int p = 0, q = 0;
    while (p < n && q < m) {
        pt tmp = det(v[p], w[q]) >= 0 ? v[p++] : w[q++];
        x.push_back(x.back() + tmp);
    }
    while (p < n) x.push_back(x.back() + v[p++]);
    while (q < m) x.push_back(x.back() + w[q++]);
    return get_convex(x);
}

例题1:CF87E Mogohu-Rea Idol

题目大意:

按逆时针顺序给出三个凸包点集 A,B,C,每次查询给出点 q,问是否存在点 a\in A,b\in B,c\in C 满足 qΔabc 的重心。

分析:

设重心为 Q,易知 OA+OB+OC=3OQ.
对三个凸包求个和,得到 P=A+B+C=\begin{Bmatrix}a+b+c|a\in A,b\in B\,c\in C\end{Bmatrix},然后直接判断点 3\cdot q 是否在 P 以内即可。

int main(int argc, const char * argv[]) {
    int a[3];
    vector<pt> _a[3];
    conv A[3];
    for (int i = 0; i < 3; i++) {
        scanf("%d", &a[i]); _a[i].resize(a[i]);
        for (auto &p : _a[i]) p.input();
        A[i] = get_convex(_a[i]);
    }

    conv res = Minkowski(A[0], Minkowski(A[1], A[2]));

    int q; scanf("%d", &q);
    while (q--) {
        pt x; x.input(); x = x * 3;
        if (containOlogn(res, x)) puts("YES");
        else puts("NO");
    }
    return 0;
}

例题2:P4557 [JSOI2018]战争

题目大意:

两个凸包 A,B,移动 B,问是否还有交点。n\le 10^5,q\le 10^5

分析:

(a\in A,b\in B) 则移动向量 w 使得存在 b+w=a,那么 w 需要满足 w=a-b。构造闵可夫斯基和C=\begin{Bmatrix}A+(-B)\end{Bmatrix},在输入 B 的时候横纵坐标都取反即可。

int main(int argc, const char * argv[]) {
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    vector<pt> a(n), b(m);
    for (int i = 0; i < n; i++) {
        a[i].input();
    }
    for (int i = 0; i < m; i++) {
        b[i].input();
        b[i] = pt() - b[i];
    }
    conv A = get_convex(a), B = get_convex(b);
    conv C = Minkowski(A, B);

    while (q--) {
        pt x; x.input();
        if (containOlogn(C, x)) puts("1");
        else puts("0");
    }
    return 0;
}

评论

  1. 2 年前
    2023-1-17 18:46:34

    Similarly, tension and mental health issues can trigger or worsen erectile dysfunction cialis online without prescription

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇