计算几何 | 凸包入门及例题

学习笔记

Andrew算法

算法做两次扫描,先从最左边的点沿下凸包扫描到最右边,再从最右边的点沿上凸包扫描到最左边,上下合在一起是完整的凸包。

例题

模板题:求凸包周长

以下convex_hull函数为核心求解凸包的函数,凸包顶点放在ch数组中,返回值为顶点数。

//计算几何
int sgn(db x) {
    
      //判断浮点数是否为0,为0时返回0
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

struct point{
    
    
    db x, y;
    point operator + (point B) {
    
    return point{
    
    x + B.x, y + B.y};}
    point operator - (point B) {
    
    return point{
    
    x - B.x, y - B.y};}
    bool operator == (point B) {
    
    return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
    bool operator < (point B) {
    
      //用于sort排序
        return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0);
    }
}p[maxn], ch[maxn];

db cross(point A, point B) {
    
    return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
    
    return hypot(A.x - B.x, A.y - B.y);}
//求凸包,凸包顶点放在ch中,返回值为顶点数
int convex_hull(point *p, int n, point *ch) {
    
    
    sort(p, p + n);  //排序
    n = unique(p, p + n) - p;  //清除重复元素
    int v = 0;
    //求下凸包
    _for(i, 0, n) {
    
    
        while (v > 1 && sgn(cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 2])) <= 0)
            v--;
        ch[v++] = p[i];
    }
    int j = v;
    //求上凸包
    for(int i = n - 2; i >= 0; --i) {
    
      //最右边的已经选中了,i从n-2开始
        while (v > j && sgn(cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 2])) <= 0) {
    
    
            v--;
        }
        ch[v++] = p[i];
    }
    v = (n > 1) ? v - 1 : v;  //若n>1,最左边的多加了一次
    return v;
}

以下部分求解周长,其中有一些小技巧:

1.求周长,到凸包上最后一个顶点与首个顶点相加的处理:取余

distance(ch[i], ch[(i + 1)%v])

2.特判一下n的个数。

       _for(i, 0, n) {
    
    
            scanf("%lf%lf", &a, &b);
            p[i] = point{
    
    a, b};
        }
        int v = convex_hull(p, n, ch);  //凸包顶点数
        db l = 0.0;
        if (n == 1) l = 0;
        else if (n == 2) l = distance(ch[0], ch[1]);
        else {
    
    
            _for(i, 0, v) l += distance(ch[i], ch[(i + 1)%v]);  //巧妙求周长
        }
        printf("%.2f\n", l);

上凸包+共线处理Interstellar Travel

解题思路

求顺时针上凸包面积最大。

注意事项

1.一开始对所有点进行排序,x相同时,y大的排在前面。

扫描二维码关注公众号,回复: 12422190 查看本文章

2.注意三点共线的情况,判断是否能使得字典序更小。

参考代码

#include<bits/stdc++.h>
using namespace std;
#define LOCAL  //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-8;  //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1e6 + 10;

int readint(){
    
    
    int x; scanf("%d", &x); return x;
}

//计算几何
int sgn(db x) {
    
      //判断浮点数是否为0,为0时返回0
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

struct point{
    
    
    db x, y;
    int pos;
    point operator + (point B) {
    
    return point{
    
    x + B.x, y + B.y};}
    point operator - (point B) {
    
    return point{
    
    x - B.x, y - B.y};}
    bool operator == (point B) {
    
    return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
    bool operator < (point B) {
    
      //用于sort排序
        if (x == B.x) {
    
    
            if (y == B.y) return pos < B.pos;
            else return y > B.y;
        } else return x < B.x;
    }
}p[maxn], ch[maxn];

db cross(point A, point B) {
    
    return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
    
    return hypot(A.x - B.x, A.y - B.y);}

bool judge(point A, point B, point C) {
    
      //判断需不需要回退
    point f = B - A, s = C - A;
    if (cross(f, s) > 0) return true;
    else if (cross(f, s) == 0) {
    
    
        return C.pos < B.pos;
    }
    else return false;
}
//求凸包,凸包顶点放在ch中,返回值为顶点数
int convex_hull(point *p, int n, point *ch) {
    
    
    sort(p, p + n);  //排序
    //n = unique(p, p + n) - p;  //清除重复元素
    int v = 0;
    //求上凸包
    for(int i = 0; i < n; ++i) {
    
    
        if (i && p[i].x == p[i - 1].x) continue;
        while (v > 1 && sgn(judge(ch[v - 2], ch[v - 1], p[i])) > 0) {
    
    
            v--;
        }
        ch[v++] = p[i];
    }
    //v = (n > 1) ? v - 1 : v;
    return v;
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int n, pos; db a, b;
    int t;
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%d", &n);
        _for(i, 0, n) {
    
    
            scanf("%lf%lf", &a, &b);
            p[i] = point{
    
    a, b, i + 1};
        }
        int v = convex_hull(p, n, ch);  //凸包顶点数
        _for(i, 0, v) {
    
    
            if (i) printf(" ");
            printf("%d", ch[i].pos);
        }
        printf("\n");
    }
    return 0;
}

拓展:数学+凸包brz的序列

巧妙地运用凸包的一道题。

参考资料

算法竞赛从入门到进阶
(真的巨巨巨巨好的一本书!!!)

猜你喜欢

转载自blog.csdn.net/Encore47/article/details/109611341
今日推荐