HDU 4273

计算凸包重心到各面的最短距离。

若知道重心,按四面体用体积法即可求出高。

关键在于,多面体重心的求法。这必须把多面体分割成多个四面体来求。下面从多边形的重心说起。

一般来用,对于一个多边形(p0,p1,p2....pn-1),其重心一般为pc.x=(p0.x+p1.x+....)/n对于y也一样。

但这其实是不正确的。反例以梯形为例。上面的式子当各点的权值均匀时是正确的。(三角形是一个特例)

但在多边形上,由于面的密度一样,所以,应当是与面积有关的。于是,把多边形分割成多个三角形,求出其重心。这样重心组成一个新的多边形与原多边形重心相同。于是,就把质量都集中在了重心上。而质量与面积相关。

于是,可由代码求重心:

以P0为顶点划分三角形,求得是有向面积,因为可以正负抵消

for(多边形上的点){  //逆时针
  与p0组成三角形。
  有向面积V=(p1-p0)*(p2-p0)/2;
  Vtot+=V;
  sum_x+=(p1.x+p2.x+p0.x)*V;
  sum_y+=(p1.y+p2.y+p0.y)*V;
  C.x=sum_x/3/Vtot; C.y=sum_y/3/Vtot
}

  对于求多面体,只需划分成四面体来求即可。增加一个z坐标,同时

sum_x+=(p1.x+p2.x+p0.x+p3.x)*V;
C.x=sum_x/4/Vtot;

  

/*
增量法求凸包。选取一个四面体,同时把它各面的方向向量向外,增加一个点时,若该点与凸包上的某些面的方
向向量在同一侧,则去掉那些面,并使某些边与新增点一起连成新的凸包上的面。
*/

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

using namespace std;
const int MAXN=110;
const double eps=1e-8;
const double inf=1e10;
struct point {
    double x,y,z;
};
struct face {
    int a,b,c;
    bool ok;
};
int n;  //初始点数
point p[MAXN]; //空间点
int trianglecnt; //凸包上三角形数
face tri[6*MAXN]; //凸包上被创建的三角形
int vis[MAXN][MAXN]; //点i到点j是属于哪一个三角形。此处是有方向

point operator -(const point &x, const point &y){
    point ret;
    ret.x=x.x-y.x; ret.y=x.y-y.y; ret.z=x.z-y.z;
    return ret;
}

point operator * (const point &u,const point &v){  //叉积
    point ret;
    ret.x=u.y*v.z-u.z*v.y;
    ret.y=u.z*v.x-u.x*v.z;
    ret.z=u.x*v.y-u.y*v.x;
    return ret;
}

double  operator ^(const point &u,const point &v){  //点积
    return (u.x*v.x+u.y*v.y+u.z*v.z);
}

double dist(point t){
    return sqrt(t.x*t.x+t.y*t.y+t.z*t.z);
}

double ptoplane(point &tmp,face &f){    //若结果大于0,证明点面的同向,即法向量方向
    point m=p[f.b]-p[f.a]; point n=p[f.c]-p[f.a];
    point t=tmp-p[f.a];
    return (m*n)^t;
}

double farea(point a,point b,point c ){
    point t1=a-c; point t2=b-c;
    return fabs(dist(t1*t2));
}
void dfs(int pt, int ct);
void deal(int pt,int a,int b){
    int f=vis[a][b];   //所属三角形,即原来的ab。
    face add;
    if(tri[f].ok){
        if((ptoplane(p[pt],tri[f]))>eps) dfs(pt,f);   //若点同样在该f三角形方向一侧,继续调整
        else {
            add.a=b; add.b=a; add.c=pt; add.ok=1;
            vis[pt][b]=vis[a][pt]=vis[b][a]=trianglecnt;
            tri[trianglecnt++]=add;
        }
    }
}

void dfs(int pt, int ct){
    tri[ct].ok=0;   //去掉该面
    deal(pt,tri[ct].b,tri[ct].a);   //因为有向边ab所属三角形去掉,则反方向边必定属于另一个三角形.
    deal(pt,tri[ct].c,tri[ct].b);
    deal(pt,tri[ct].a,tri[ct].c);
}

void construct (){
    int i,j;
    trianglecnt=0;
    if(n<4) return ; //不可能构成一个多面体
    bool tmp=true;
    for(i=1;i<n;i++){    //不共点两点
        if(dist(p[0]-p[i])>eps){
            swap(p[1],p[i]); tmp=false; break;
        }
    }
    if(tmp) return ;
    tmp=true;
    for(i=2;i<n;i++){   //不共线
        if(dist((p[0]-p[1])*(p[1]-p[i]))>eps){
            swap(p[2],p[i]); tmp=false; break;
        }
    }
    if(tmp) return ;
    tmp=true;
    for(i=3;i<n;i++){   //四点不共面K
        if(fabs((p[0]-p[1])*(p[1]-p[2])^(p[0]-p[i]))>eps){
            swap(p[3],p[i]); tmp=false; break;
        }
    }
    if(tmp) return ;
    face add;
    for(i=0;i<4;i++){   //使各三角形的方向向量向外,同时记录下三角形的序号
        add.a=(i+1)%4; add.b=(i+2)%4; add.c=(i+3)%4; add.ok=1;  //等于1表示在凸包上
        if(ptoplane(p[i],add)>0) swap(add.b,add.c);
        vis[add.a][add.b]=vis[add.b][add.c]=vis[add.c][add.a]=trianglecnt;
        tri[trianglecnt++]=add;
    }
    for(i=4;i<n;i++){   //构建凸包
        for(j=0;j<trianglecnt;j++){
            if(tri[j].ok&&(ptoplane(p[i],tri[j]))>eps){  //增加点可见该平,即在面方向一侧
                dfs(i,j); break;
            }
        }
    }
    int cnt=trianglecnt;
    trianglecnt=0;
    for(i=0;i<cnt;i++){    //只有ok为1的才属于凸包上的三角形
        if(tri[i].ok){
            tri[trianglecnt++]=tri[i];
        }
    }
}

double cdis(point p0){
	double ans=inf;
	point p1,p2,p3;
	for(int i=0;i<trianglecnt;i++){
		p1=p[tri[i].a]; p2=p[tri[i].b];
	 	p3=p[tri[i].c];
		double V=fabs(((p0-p1)^((p2-p1)*(p3-p1)))/6);
	//	printf("%lf\n",V);
		ans=min(ans,V*3*2/dist((p2-p1)*(p3-p1)));
	}
	return ans;

}

point Cenconstruct(){
	point p0=p[0];
	point p1,p2,p3; double sum_area=0,sum_x=0,sum_y=0,sum_z=0;
	for(int i=0;i<trianglecnt;i++){
		p1=p[tri[i].a]; p2=p[tri[i].b]; p3=p[tri[i].c];
		double V=((p0-p1)^((p2-p1)*(p3-p1)))/6;
		sum_area+=V;
		sum_x+=(p0.x+p1.x+p2.x+p3.x)*V;
		sum_y+=(p0.y+p1.y+p2.y+p3.y)*V;
		sum_z+=(p0.z+p1.z+p2.z+p3.z)*V;
	}
	point ret;
	ret.x=sum_x/4/sum_area; ret.y=sum_y/4/sum_area; ret.z=sum_z/4/sum_area;
	return ret;
}
int main(){
    while(scanf("%d",&n)!=EOF){
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)
        scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        construct();
        point centroid=Cenconstruct();
        double ans;
        ans = cdis(centroid);
        printf("%.3lf\n",ans);
    }
}

  

HDU 4273

时间: 2024-10-05 06:12:20

HDU 4273的相关文章

hdu 4273 2012长春赛区网络赛 三维凸包中心到最近面距离 ***

新模板 1 /* 2 HDU 4273 Rescue 3 给一个三维凸包,求重心到表面的最短距离 4 模板题:三维凸包+多边形重心+点面距离 5 */ 6 7 #include<stdio.h> 8 #include<algorithm> 9 #include<string.h> 10 #include<math.h> 11 #include<stdlib.h> 12 using namespace std; 13 const int MAXN=

HDU 4273 Rescue(三维凸包 + 重心)

Rescue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 398    Accepted Submission(s): 296 Problem Description I work at NASA outer space rescue team which needs much courage and patient. In dai

hdu 4274 Spy&amp;#39;s Work(水题)

Spy's Work Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1266    Accepted Submission(s): 388 Problem Description I'm a manager of a large trading company, called ACM, and responsible for the

hdu 4274 Spy&#39;s Work(水题)

Spy's Work Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1266    Accepted Submission(s): 388 Problem Description I'm a manager of a large trading company, called ACM, and responsible for the

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是