【基本知識】
向量運算:
模長:
加減:對應座標加減,結果爲一個向量;
點積:依舊定義爲到的投影,計算方式爲將對應座標相乘後相加,結果爲一個值。如
。
叉積:**叉積的結果是一個向量。**方向可用“右手定則”~~(瞎編的名字)~~確定:食指指向一個向量,中指指向另一個,大拇指與中指、食指所在平面垂直。則大拇指的方向即爲叉積結果的方向。
座標計算:
平面的法向量:見向量叉積的定義。
基本操作:
·判斷點D是否在平面ABC內: 求平面ABC的法向量,若,則。
點D到平面ABC距離: 由向量點積的定義:
算法(持續更新):
求凸包。
step1:擾動。
將每一個點的座標加/減一個足夠小的隨機值,使其不會引起誤差,並能保證不會出現四點共面的情況。
step2:記錄平面
由於最多三點在一個平面上,故通過記錄平面上的三個點記錄一個平面。
step3:構造
假如我們構造出了一個凸包。現在嘗試擴展。
我們從待選點中拿一個出來,記爲。從向凸包作射線。
不難發現,這若干條射線一定與凸包交於若干個棱上。
將可見面刪去,並將加入凸包。這樣,我們就完成了一次擴展。
爲保證正確性,需要對每一個平面判斷是否可見,故時間複雜度爲。
判斷某個點是否可見:用平面的法向量與P到平面上的任意一點做點積。若結果(即夾角爲銳角),則該平面可見;反之不可見。
#include<bits/stdc++.h>
using namespace std;
const int mn = 2005;
const double eps = 1e-10;
double Rand() {return rand() / (double) RAND_MAX;}
double reps() {return (Rand() - 0.5) * eps;}
struct point{
double x, y, z;
void shake() {x += reps(), y += reps(), z += reps();}
double len() {return sqrt(x * x + y * y + z * z);}
point operator -(const point b) const {return (point) {x - b.x, y - b.y, z - b.z};}
point operator *(const point b) const {return (point) {y * b.z - b.y * z, z * b.x - b.z * x, x * b.y - b.x * y};}
double operator&(const point b) const {return x * b.x + y * b.y + z * b.z;}
}p[mn];
struct surf{
int v[3];
point normal() {return (p[v[1]] - p[v[0]]) * (p[v[2]] - p[v[0]]);}
double S() {return normal().len() / 2.0;}
}f[mn], c[mn];
int n, cnt, vis[mn][mn];
int check(surf f, point pp) {return ((pp - p[f.v[0]]) & f.normal()) > 0;}
inline void convey()
{
f[++cnt] = (surf) {1, 2, 3}, f[++cnt] = (surf) {3, 2, 1};
for(int i = 4, tmp = 0; i <= n; i++)
{
for(int j = 1, v; j <= cnt; j++)
{
if(!(v = check(f[j], p[i]))) c[++tmp] = f[j];
for(int k = 0; k < 3; k++) vis[f[j].v[k]][f[j].v[(k + 1) % 3]] = v;
}
for(int j = 1; j <= cnt; j++)
for(int k = 0; k < 3; k++)
{
int x = f[j].v[k], y = f[j].v[(k + 1) % 3];
if(vis[x][y] && !vis[y][x]) c[++tmp] = (surf) {x, y, i};
}
for(int j = 1; j <= tmp; j++) f[j] = c[j];
cnt = tmp, tmp = 0;
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z), p[i].shake();
convey(); double ans = 0;
for(int i = 1; i <= cnt; i++) ans += f[i].S();
printf("%.6f\n", ans);
}