(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; }