1.向量点积同二维,x1y1+x2y2+x3y3。向量叉积是行列式形式,(y1z2-z1y2,z1x2-x1z2,x1y2-y1x2)。

2.增量构造法

  1)首先定义,一个平面由三个点唯一确定。一个平面是有方向的,它的法向量只有一个方向(即逆时针相邻两向量的叉积的方向)。

  2)初始时只有(p1,p2,p3)和(p3,p2,p1)两个平面(相当于两个方向相反的面组成了一个体积为0的凸包)

  3)每次加入一个新点时,以这个点为光源中心投影到凸包上,不能被照到的面在新凸包中仍然存在,否则不存在。

  4)将新点和明暗分界线上的边组成的面加入凸包。

3.具体实现

  1)一个面是否能被照到的判断:点在这个面的正面,即平面上任意一点到这个点的向量与平面法向量的点积为正值。

  2)明暗分界线的判断:每次判断一个面能否被照到时,标记vis[p1][p2]表示p1p2这条边的某边是否被照到。若vis[p1][p2]!=vis[p2][p1]则说明它是分界线。

  3)三位面积计算:两个向量作三维叉积后将模长除2即可。

 

4.精度问题

  精度是计几永恒不变的话题。

  考虑四点共面的情况,在上述做法中,加入其中三个点组成的平面后,第四个点会因为找不到这个平面而被忽略,事实上第四个点可能会增加这个面的面积。

  为了避免这种情况,我们将每个点都作一些微小的“扰动”,这样就几乎不可能有四点共面的情况。同时,这个扰动幅度不能过大,不能影响到输出要求的精度。

  但是同时,由于这种扰动,如果输入数据中有多个相同的点,在扰动幅度设的不好的情况下,会因为精度误差使所有坐标相同的点全部被认为使不同的点,从而导致凸包面数指数型增长。

  实测扰动范围在1e-8左右可以同时一定程度上有效避免这两个问题,但一定也能被卡掉。也就是说网上大部分代码是可以轻松被卡掉的。具体应该怎么做才能避免精度问题感觉十分困难。

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #define rep(i,l,r) for (int i=(l); i<=(r); i++)
  5. using namespace std;
  6.  
  7. const int N=;
  8. const double eps=1e-;
  9. double ans;
  10. int n,cnt,tot;
  11. bool vis[N][N];
  12. double rs() {return (rand()/(double)RAND_MAX-0.5)*eps;}
  13.  
  14. struct P{ double x,y,z; }p[N];
  15. double len(P a){ return sqrt(a.x*a.x+a.y*a.y+a.z*a.z); }
  16. void shake(P &a){ a.x+=rs(); a.y+=rs(); a.z+=rs(); }
  17. P operator -(const P &a,const P &b){ return (P){a.x-b.x,a.y-b.y,a.z-b.z}; }
  18. P operator *(const P &a,const P &b){ return (P){a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x}; }
  19. double mul(const P &a,const P &b){ return a.x*b.x+a.y*b.y+a.z*b.z; }
  20.  
  21. struct F{ int v[]; }f[N],tmp[N];
  22. P Normal(F &a){ return (p[a.v[]]-p[a.v[]])*(p[a.v[]]-p[a.v[]]); }
  23. double area(F &a){ return len(Normal(a))/.; }
  24. bool see(F &a,P &b){ return mul(b-p[a.v[]],Normal(a))>; }
  25.  
  26. void Convex(){
  27. f[++tot]=(F){,,}; f[++tot]=(F){,,};
  28. rep(i,,n){
  29. int tot1=;
  30. rep(j,,tot){
  31. bool fl=see(f[j],p[i]);
  32. if (!fl) tmp[++tot1]=f[j];
  33. rep(k,,) vis[f[j].v[k]][f[j].v[(k+)%]]=fl;
  34. }
  35. rep(j,,tot) rep(k,,){
  36. int x=f[j].v[k],y=f[j].v[(k+)%];
  37. if (vis[x][y] && !vis[y][x]) tmp[++tot1]=(F){x,y,i};
  38. }
  39. tot=tot1; rep(j,,tot1) f[j]=tmp[j];
  40. }
  41. }
  42.  
  43. int main(){
  44. scanf("%d",&n);
  45. rep(i,,n) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z),shake(p[i]);
  46. Convex();
  47. rep(i,,tot) ans+=area(f[i]); printf("%.3lf\n",ans);
  48. return ;
  49. }

[Luogu4724][模板]三维凸包(增量构造法)的更多相关文章

  1. BZOJ1209 最佳包裹 (三维凸包 增量法)

    题意 求三维凸包的表面积. N≤100N\le100N≤100 题解 暴力往当前的凸包里加点.O(n2)O(n^2)O(n2).题解详见大佬博客 扰动函数shakeshakeshake是为了避免四点共 ...

  2. luogu P4724 模板 三维凸包

    LINK:三维凸包 一个非常古老的知识点.估计也没啥用. 大体上了解了过程 能背下来就背下来吧. 一个bf:暴力枚举三个点 此时只需要判断所有的点都在这个面的另外一侧就可以说明这个面是三维凸包上的面了 ...

  3. Luogu 4724 三维凸包

    Luogu 4724 三维凸包 增量法,维护当前凸包,每次加入一个点 \(P\) ,视其为点光源,将可见面删去,新增由"晨昏线"(分割棱)与 \(P\) 构成的平面. 注意每个平面 ...

  4. hdu4266(三维凸包模板题)

    /*给出三维空间中的n个顶点,求解由这n个顶点构成的凸包表面的多边形个数. 增量法求解:首先任选4个点形成的一个四面体,然后每次新加一个点,分两种情况: 1> 在凸包内,则可以跳过 2> ...

  5. POJ3528 HDU3662 三维凸包模板

    POJ3528 HDU3662 第一道题 给定若干点 求凸包的表面积,第二题 给定若干点就凸包的面数. 简单说一下三维凸包的求法,首先对于4个点假设不共面,确定了唯一四面体,对于一个新的点,若它不在四 ...

  6. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

  7. POJ 3528--Ultimate Weapon(三维凸包)

    Ultimate Weapon Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 2430   Accepted: 1173 ...

  8. BZOJ1209 [HNOI2004]最佳包裹 三维凸包 计算几何

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1209 题目概括 给出立体的n个点.求三维凸包面积. 题解 增量法,看了一天,还是没有完全懂. 上板 ...

  9. POJ 2225 / ZOJ 1438 / UVA 1438 Asteroids --三维凸包,求多面体重心

    题意: 两个凸多面体,可以任意摆放,最多贴着,问他们重心的最短距离. 解法: 由于给出的是凸多面体,先构出两个三维凸包,再求其重心,求重心仿照求三角形重心的方式,然后再求两个多面体的重心到每个多面体的 ...

随机推荐

  1. Vue学习看这篇就够

    Vue -渐进式JavaScript框架 介绍 vue 中文网 vue github Vue.js 是一套构建用户界面(UI)的渐进式JavaScript框架 库和框架的区别 我们所说的前端框架与库的 ...

  2. 容斥原理&&莫比乌斯专题

    A题:A - Eddy's爱好   HDU - 2204 具体思路:如果是求n中,为平方数的有多少个,那么答案肯定是sqrt(n),同理,如果是三次根号的话,那么答案肯定是n的三分之一次方.然后继续按 ...

  3. 2016.5.15——leetcode:Number of 1 Bits ,

    leetcode:Number of 1 Bits 代码均测试通过! 1.Number of 1 Bits 本题收获: 1.Hamming weight:即二进制中1的个数 2.n &= (n ...

  4. 【驱动】USB驱动实例·串口驱动·键盘驱动【转】

    转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_drive ...

  5. JSP中page,request,session,application四个域对象区别

    page page指当前页面.只在一个jsp页面里有效 . page里的变量没法从index.jsp传递到test.jsp,只要页面跳转了,它们就不见了. pageContext 如果把变量放到pag ...

  6. GitHub安装和使用

    GitHub是一个基于git的代码托管平台,付费用户可以建私人仓库,一般的免费用户只能使用公共仓库,也就是代码要公开. Github 由Chris Wanstrath, PJ Hyett 与Tom P ...

  7. 分析new delete 的本质

    在程序设计中,数据可能会存在不同的内存空间,如函数栈 堆   全局变量区  ,今天我们来分析一下C++中堆分配方式和C语言的堆分配方式异同,从而更好的理解new  delete本质 C语言使用mall ...

  8. python基础学习之路No.4 数据转换以及操作

    练习python的时候经常会用到一些不同数据类型之间的转换操作 搜集了一些资料,整理如下 函数 描述 int(x [,base]) 将x转换为一个整数 long(x [,base] ) 将x转换为一个 ...

  9. vi/vim基本使用方法(转)

    转自:http://www.cnblogs.com/itech/archive/2009/04/17/1438439.html vi/vim 基本使用方法 本文介绍了vi (vim)的基本使用方法,但 ...

  10. Windbg在应用层调试漏洞时的应用

    主要记录一些在应用层调试漏洞的技巧,不会写一些基本的命令,只记录比较有用的平时难以想到的调试方法. 1.!address eax 查看对应内存页的属性,如果poc触发异常之后就可以用这个指令看一下触发 ...