AcWing 2119. 最佳包裹 (三维几何基础三维凸包)

三维凸包模板

增量算法 复杂度O(n^n)

枚举点集 每次看点与当前所有面的关系 Pr能照到的面删除 剩下的保留搭配np[]

再将交界处的边与Pr的连接面加入np[]

注意需要对数据微小扰动 防止存在四点共面

#include<cstring>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;
const int MaxN = 105;
const double eps = 1e-12;

int n,m;//多面体中n个点 m个边
bool vis[MaxN][MaxN];//vis[i][j]:P在i->j面的上面

double rand_eps(){//随机微小扰动 防止四点共面
	return ((double)rand() / RAND_MAX - 0.5) * eps;//[-0.5,0.5]*eps
}

struct Point{
	double x,y,z;
	void shake(){
		x += rand_eps();
		y += rand_eps();
		z += rand_eps();
	}
	double len(){
		return sqrt(x * x + y * y + z * z);
	}
	Point operator - (Point t){
		return {x - t.x,y - t.y,z - t.z};
	}

	double operator & (Point t){//点积
		return (x*t.x + y*t.y + z*t.z);
	}
	Point operator * (Point t){//叉积
		return {y*t.z - z*t.y, z*t.x - x*t.z, x*t.y - y*t.x};
		//AxB = (y1z2 - z1y2, z1x2 - x1z2, x1y2 - x2y1)
	}
}p[MaxN];

struct Plane{
	int v[3];//构成平面的三个点的序号
	Point norm(){//平面法向量 A ×B
		return (p[v[1]] - p[v[0]]) * (p[v[2]] - p[v[0]]);
	}
	double area(){//平面的面积
		return norm().len() / 2;//叉积的模长==平行四边形面积
	}
	bool above(Point a){//点a是否在平面上方
		return ((a - p[v[0]]) & norm()) >= 0;//不要再用dcmp啦 不然微小扰动就没意义了
	}
}plane[MaxN << 1],np[MaxN << 1];

void get_convex_3d(){
//	plane[m++] = {0,1,2};
//	plane[m++] = {2,1,0};
	plane[m].v[0] = 0,plane[m].v[1] = 1,plane[m].v[2] = 2;
	m++;
	plane[m].v[0] = 2,plane[m].v[1] = 1,plane[m].v[2] = 0;
	m++;
	for(int i = 3;i < n; i++){
		int cnt = 0;
		for(int j = 0;j < m; j++){
			bool up = plane[j].above(p[i]);//点i在平面j的上面? 
			if(!up) np[cnt++] = plane[j];//不在上面 该平面保留
			for(int k = 0;k < 3; k++){
				vis[plane[j].v[k]][plane[j].v[(k+1)%3]] = up;
			}//把顺逆两个平面中a->b b->a的状态存下来 以便找出处于阴暗交接的边
		}
		for(int j = 0;j < m ;j++){
			for(int k = 0;k < 3; k++){
			//枚举现在每个平面中的边 确定那些边需要与新点i组成添加进plane[]的面
				int a = plane[j].v[k],b = plane[j].v[(k+1)%3];
				if(vis[a][b] && !vis[b][a]){
//					np[++cnt] = {a,b,i};
					np[cnt].v[0] = a,np[cnt].v[1] = b,np[cnt].v[2] = i;
					cnt++;
				}
			}
		}
		m = cnt;
		for(int j = 0;j < m; j++){
			plane[j] = np[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);
		p[i].shake();
	}
	get_convex_3d();//三维凸包
	double ans = 0.0;
	for(int i = 0;i < m; i++){
		ans += plane[i].area();
	}
	printf("%.6f\n",ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值