最佳包裹
求三维凸包用增量法求解
假设我们已经维护好了前面的点的凸包,对于新加入的点:
如果这个点在凸包内部显然那就不用管了
如果这个点在凸包外部,那么考虑如下情况:
将新点
如何判断一个面是否能够被照到:取多面体外侧为正方向,则对于某一个面,如果光源在其正侧那么能被照到,否则不行;具体判断的时候,在平面上取一个点
维护面的时候,我们都是维护的三角形(如果一个面不是三角形我们对其进行三角剖分),存储三角形的三个点就好了(按照逆时针方向存储);一共有
如何找出分界线:设
我们只需要判断
代码见下:
#include<bits/stdc++.h>
#define ll long long
#define Vector Point
using namespace std;
const int N=2010;
const double eps=1e-10;
double Rand()
{
return rand()/(double)RAND_MAX;
//RAND_MAX是宏定义,表示rand()能返回的最大整数
}
double reps()
{
return (Rand()-0.5)*eps;
}
struct Point
{
double x,y,z;
Point(double a=0,double b=0,double c=0):x(a),y(b),z(c){}
void shake(){x+=reps(),y+=reps(),z+=reps();}
}p[N];
Vector Cross(Vector a,Vector b)
{
return Vector{a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x};
}
bool vis[N][N];
int cnt,n;
Point operator - (Point a,Point b){return Point(a.x-b.x,a.y-b.y,a.z-b.z);}
double get_length(Vector a)
{
return sqrt(a.x*a.x+a.y*a.y+a.z*a.z);
}
struct Face
{
int v[3];//存储这一个面的三个点的编号,逆时针方向
double area(){return get_length(Cross(p[v[1]]-p[v[0]],p[v[2]]-p[v[0]]))/2;}
}f[N<<2],C[N<<2];
double operator * (Vector a,Vector b){return a.x*b.x+a.y*b.y+a.z*b.z;}
bool see(Face a,Point b)
{
return (b-p[a.v[0]])*Cross(p[a.v[1]]-p[a.v[0]],p[a.v[2]]-p[a.v[0]])>0;//进行了微小扰动,不会出现等于0的情况
}
void Convex_3D()
{
f[++cnt]={1,2,3},f[++cnt]={3,2,1};
//对于前三个点的凸包,我们认为是两张一模一样的平面,但是方向不同
//如果{1,2,3}从某一个方向看是顺时针,那么我们就认为我们从另一个方向看
//总之可以找到一个方向使得{1,2,3}为逆时针,{3,2,1}是顺时针
//于是此时认为这个方向为正方向,{3,2,1}的正方向就是这个方向的反方向
for(int i=4,cc=0;i<=n;i++)
{
bool v;
for(int j=1;j<=cnt;j++)
{
v=see(f[j],p[i]);//判断是否看得见
if(!v) C[++cc]=f[j];
for(int k=0;k<3;k++)
vis[f[j].v[k]][f[j].v[(k+1)%3]]=v;
//vis[i][j]用来记录向量ij所表示的面(左手边)是否被看得见
}
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[++cc]={x,y,i};
//添加新的面,注意三个点的顺序,要保证逆时针
}
for(int j=1;j<=cc;j++) f[j]=C[j];
cnt=cc;
cc=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();//这一步在随机微小扰动,为什么要下文说明
}
Convex_3D();
double ans=0;
for(int i=1;i<=cnt;i++) ans+=f[i].area();
printf("%.3f\n",ans);
return 0;
}
说明一下为什么要微小扰动:这是为了防止出现四点共面的情况。如果出现了四点共面的情况,可能会导致各种错误。举一个例子,比如对于前四个点共面,那么在加入第四个点的时候,由上面的代码判断的是之前三个点形成的凸包的两个面都看不见,就都会算进去,而且现在不知道新加入的三角形如何剖分(剖分方式很多),所以不行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构