HDU-4449 三维凸包 平面投影 三维坐标转二维坐标 二维凸包

作为建筑师,你需要设计一个与给定凸多面体形状相同的建筑,并使其最高点尽可能高,同时地面投影面积最小。输入包含多面体顶点信息,需计算最大高度H和最小投影面积S。解决方案包括计算三维凸包,枚举底面,转换二维坐标并求二维凸包以确定投影面积。

Problem Description
You are a notable architect.
Recently, a company invites you to design their new building for them. It is not an easy task, because they make some strange claims to the new building:
1.Its shape should be the same as a convex polyhedron which they have given to you, though you can rotate it arbitrarily.
2.One face of the building should cling to the ground. (Of course! Have you ever seen a building only touch the ground by an edge, or a point, or even suspend in the air?)
3.They want the building to be as striking as possible. We call the highest point of the building as the “peak”. The higher the peak is the more striking the building will be.
4.If there are many designs meet all claims above, the occupied land of the building should be less. In another word, the building’s vertical projection area on the ground should be as small as possible.
Now, give you the relative positions of vertices of the building, please design the building and tell them H – the height of the peak, as well as S - the projection area of the building in your best design.

Input
There are several test cases in the input.
Each test case begins with one integer n (1 ≤ n ≤ 50), indicating the number of vertices of the building.
Then n lines follow. Each line contains three integers x, y, z (-10000 ≤ x, y, z ≤ 10000), separated by spaces, indicating the relative positions of one vertex of the building.
The input ends with n = 0.

Output
For each test case, output two float numbers H and S in one line, separated by one space.
Please round the results to three digits after decimal point.

Sample Input
6
1 0 0
-1 0 0
0 1 0
0 -1 0
0 0 1
0 0 -1
0

Sample Output
1.155 1.732

先算出三维凸包
枚举凸包每个面为底面,算高度
高度大更新高度和投影面积答案
高度相等时更新投影面积答案
投影面积,把每个点向底面做投影
把每个投影点的坐标转换成二维坐标求凸包 (方法)
在求凸包面积即可
三维凸包

const int MAXN=5050;
struct CH3D{//三维凸包
	struct face{
		int a,b,c;//表示凸包一个面上的三个点的编号
		bool ok;//表示该面是否属于最终的凸包上的面
	};
	Point3 P[MAXN];
	int n;//初始顶点数
	int num;//凸包表面的三角形数
	face F[8*MAXN];//凸包表面的三角形
	int g[MAXN][MAXN];
	Point3 cross(Point3 a,Point3 b,Point3 c){return (b-a)^(c-a);}//叉乘
	double area(Point3 a,Point3 b,Point3 c){return ((b-a)^(c-a)).len();}//三角形面积*2
	double volume(Point3 a,Point3 b,Point3 c,Point3 d){return ((b-a)^(c-a))*(d-a);}//四面体有向面积*6
	double dblcmp(Point3 &p,face &f){//正:点在面同向
		Point3 p1=P[f.b]-P[f.a];
		Point3 p2=P[f.c]-P[f.a];
		Point3 p3=p-P[f.a];
		return (p1^p2)*p3;
	}
	void deal(int p,int a,int b){
		int f=g[a][b];
		face add;
		if(F[f].ok){
			if(dblcmp(P[p],F[f])>eps)dfs(p,f);
			else{
				add.a=b;add.b=a;add.c=p;add.ok=true;
				g[p][b]=g[a][p]=g[b][a]=num;F[num++]=add;
			}
		}
	}
	void dfs(int p,int now){//递归搜索所有应该从凸包内删除的面
		F[now].ok=false;
		deal(p,F[now].b,F[now].a);
		deal(p,F[now].c,F[now].b);
		deal(p,F[now].a,F[now].c);
	}
	bool same(int s,int t){
		Point3 &a=P[F[s].a];
		Point3 &b=P[F[s].b];
		Point3 &c=P[F[s].c];
		return fabs(volume(a,b,c,P[F[t].a]))<eps&&fabs(volume(a,b,c,P[F[t].b]))<eps&&fabs(volume(a,b,c,P[F[t].c]))<eps;
	}
	void create(){//构建三维凸包
		num=0;
		face add;//此段是为了保证前四个点不共面
		bool flag=true;
		for(int i=1;i<n;++i)
			if(!(P[0]==P[i])){swap(P[1],P[i]);flag=false;break;}
		if(flag)return;
		flag=true;
		for(int i=2;i<n;++i)
			if(((P[1]-P[0])^(P[i]-P[0])).len()>eps){
				swap(P[2],P[i]);flag=false;break;
			}
		if(flag)return;
		flag=true;
		for(int i=3;i<n;++i)
			if(fabs(((P[1]-P[0])^(P[2]-P[0]))*(P[i]-P[0]))>eps){
				swap(P[3],P[i]);flag=false;break;
			}
		if(flag)return;//**********************************
		for(int i=0;i<4;++i){
			add.a=(i+1)%4;add.b=(i+2)%4;add.c=(i+3)%4;add.ok=true;
			if(dblcmp(P[i],add)>0)swap(add.b,add.c);
			g[add.a][add.b]=g[add.b][add.c]=g[add.c][add.a]=num;
			F[num++]=add;
		}
		for(int i=4;i<n;++i)for(int j=0;j<num;++j)
			if(F[j].ok&&dblcmp(P[i],F[j])>eps){
				dfs(i,j);break;
			}
		int tmp=num;num=0;
		for(int i=0;i<tmp;++i)if(F[i].ok)F[num++]=F[i];
	}
	double area(){//表面积,测试:HDU3528
		double res=0;
		if(n==3){
			Point3 p=cross(P[0],P[1],P[2]);
			return p.len()/2;
		}
		for(int i=0;i<num;++i)res+=area(P[F[i].a],P[F[i].b],P[F[i].c]);
		return res/2.0;
	}
	double volume(){
		double res=0;
		Point3 tmp=Point3(0,0,0);
		for(int i=0;i<num;++i)res+=volume(tmp,P[F[i].a],P[F[i].b],P[F[i].c]);
		return fabs(res/6);
	}
	int triangle(){return num;}//表面三角形个数
	int polygon(){//表面多边形个数,测试:HDU3662
		int res=0;
		for(int i=0;i<num;++i){
			bool flag=true;
			for(int j=0;j<i;++j)if(same(i,j)){flag=0;break;}
			res+=flag;
		}return res;
	}
	Point3 barycenter(){//重心测试:HDU4273
		Point3 o=Point3(0,0,0),ans=o;
		double all=0;
		for(int i=0;i<num;++i){
			double vol=volume(o,P[F[i].a],P[F[i].b],P[F[i].c]);
			ans=ans+(((o+P[F[i].a]+P[F[i].b]+P[F[i].c])/4.0)*vol);
			all+=vol;
		}
		return ans/all;
	}
	double ptoface(Point3 p,int i){//点到面的距离,测试:HDU4273
		double tmp1=fabs(volume(P[F[i].a],P[F[i].b],P[F[i].c],p));
		double tmp2=((P[F[i].b]-P[F[i].a])^(P[F[i].c]-P[F[i].a])).len();
		return tmp1/tmp2;
	}
	void input(int n0){n=n0;for(int i=0;i<n;++i)P[i].input();}
};

主函数

int main(){
	int n;
	while(scanf("%d",&n),n){
		A.input(n);
		if(n<3){puts("0.000 0.000");continue;}
		if(n==3){printf("0.000 %.3lf\n",((A.P[2]-A.P[0])^(A.P[1]-A.P[0])).len()/2.0);continue;}
		A.create();
		double H=0,Ss=inf;
		for(int i=0;i<A.num;++i){
			int a=A.F[i].a,b=A.F[i].b,c=A.F[i].c;
			Plane sp(A.P[a],A.P[b],A.P[c]);
			double h=0;
			for(int j=0;j<A.n;++j){
				p[j]=sp.pointtoplane(A.P[j]);
				double dis=p[j].distance(A.P[j]);
				if(sgn(h-dis)<0)h=dis;
			}
			if(sgn(H-h)>0)continue;
			B.n=0;
			Point3 O=A.P[a],S=A.P[b];
			Point3 x=(S-O).trunc(1);
			Point3 y=((S-O)^sp.o).trunc(1);
			for(int j=0;j<A.n;++j)
				B.add(Point(x*(A.P[j]-O),y*(A.P[j]-O)));
			B.getconvex(C);
			double s=C.getarea();
			if(sgn(h-H)>0){
				H=h,Ss=s;
			}else if(sgn(h-H)==0){
				if(sgn(Ss-s)>0)Ss=s;
			}
		}
		printf("%.3lf %.3lf\n",H,Ss);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值