三维凸包模板

(1)暴力(n4方)可过100个点的数据

  • 给整数点加扰动,防止多点共面共线

  • 面积等于0.5*向量叉积

  • 思路:枚举所有平面,所有点都在一侧的平面为凸包平面

  • 可能卡:随机扰动之前要去重,否则三点重合扰动之后会出现极小的面

    #include <bits/stdc++.h>
    const double eps = 1e-19;
    using namespace std;
    int n, i, j, k, c0, c1;
    double now, ans;
    struct P {
        double x, y, z;
    } a[110], tmp;
    double len(const P& a) { return sqrt(a.x * a.x + a.y * a.y + a.z * a.z); }
    P operator+(const P& a, const P& b) { return (P){ a.x + b.x, a.y + b.y, a.z + b.z }; }
    P operator-(const P& a, const P& b) { return (P){ a.x - b.x, a.y - b.y, a.z - b.z }; }
    P operator&(const P& a, const P& b) { return (P){ a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; }
    double operator|(const P& a, const P& b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
    bool operator!=(const P& a, const P& b) { return a.x != b.x || a.y != b.y || a.z != b.z; }
    bool check(int i, int j, int k) {
        tmp = (a[j] - a[i]) & (a[k] - a[i]);
        c0 = c1 = 0;
        for (int l = 1; l <= n; l++)
            if (l != i && l != j && l != k) {
                now = tmp | (a[l] - a[i]);
                if (now > 0)
                    c1++;
                if (now < 0)
                    c0++;
                if (c0 > 0 && c1 > 0)
                    return 0;
            }
        return 1;
    }
    int main() {
        for (scanf("%d", &n), i = 1; i <= n; i++)
            scanf("%lf%lf%lf", &a[i].x, &a[i].y, &a[i].z),
                a[i] = a[i] + (P){ rand() * eps, rand() * eps, rand() * eps };
        for (i = 1; i <= n; i++)
            for (j = i + 1; j <= n; j++)
                for (k = j + 1; k <= n; k++)
                    if (check(i, j, k))
                        ans += len((a[k] - a[i]) & (a[j] - a[i]));
        return !printf("%lf\n", ans * 0.5);
    }

    (2)三维凸包算法(增量法)(时间复杂度: O ( n 2 ) )

  • 凸包的面的方向是按照右手螺旋的方向

  • 思路:枚举当前凸包的每个面,标记是否被一个点照到,一条边两个面,一个面被照到另一个不被照到就更新被照到的面。

  • using namespace std;
    
    typedef long long LL;
    const int N = 210;
    const double eps = 1e-12;
    int dcmp(double a, double b){
        if(fabs(a - b) < eps) return 0;
        if(a < b) return -1;
        return 1;
    }
    
    double rand_eps() { return ((double)rand() / RAND_MAX - 0.5) * eps; }
    
    struct point{
        double x, y, z;
        void shake() { x += rand_eps(); y += rand_eps(); z += rand_eps(); }
        bool operator < (point b){
            if(dcmp(x, b.x)) return dcmp(x, b.x) < 0;
            if(dcmp(y, b.y)) return dcmp(y, b.y) < 0;
            return z < b.z;
        }
        bool operator == (point b){
            return !dcmp(x, b.x) && !dcmp(y, b.y) && !dcmp(z, b.z);
        }
    }P[N];
    
    point operator + (point a, point b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
    point operator - (point a, point b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
    point cross(point a, point b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; }
    double dot(point a, point b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
    double length(point a) { return sqrt(a.x * a.x + a.y * a.y + a.z * a.z); }
    
    
    struct plane{
        int v[3];
        
        point norm() { return cross(P[v[1]] - P[v[0]], P[v[2]] - P[v[0]]); }
        double area() { return length(norm()) / 2; }
        bool above(point a) { return dot(a - P[v[0]], norm()) >= 0; }
       
    }E[N], tmp[N];
    
    int n, m;
    bool g[N][N];
    
    void get_convex_3d()
    {
        E[m++] = {0, 1, 2}; E[m++] = {2, 1, 0};
        for(int i=3; i<n; i++)
        {
            int cnt = 0;
            for(int j=0; j<m; j++)
            {
                bool t = E[j].above(P[i]);
                if(!t) tmp[cnt++] = E[j];
                for(int k=0; k<3; k++)
                    g[E[j].v[k]][E[j].v[(k + 1) % 3]] = t;
            }
            for(int j=0; j<m; j++)
                for(int k=0; k<3; k++)
                {
                    int a = E[j].v[k], b = E[j].v[(k + 1) % 3];
                    if(g[a][b] && !g[b][a]) tmp[cnt++] = {a, b, i}; // 第j面被照到,删面
                }
            m = cnt;
            for(int j=0; j<m; j++) E[j] = tmp[j];
        }
    }
    
    
    int main()
    {
        scanf("%d", &n);
        for(int i=0; i<n; i++) scanf("%lf %lf %lf", &P[i].x, &P[i].y, &P[i].z);
        
        sort(P, P + n);
        n = unique(P, P + n) - P;
        for(int i=0; i<n; i++) P[i].shake(); 
    
        get_convex_3d();
    
        double ans = 0;
        for(int i=0; i<m; i++)
            ans += E[i].area();
        
        printf("%.6f\n", ans);
        return 0;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值