很长一段时间没有写总结了,随着冬令营的结束,最近对大幅度的总结。
最后一天LRJ讲了下计算几何我才发现3D凸包原来如此简单。
主要讲了两个算法:包裹法和增量算法。
个人感觉增量法比较好,整个过程只用到了+-*这集中运算,不涉及实数运算。
包裹法:首先确定一条一定为凸包上的线段,然后由这条线段为轴,像包纸一样的往顺时针(逆时针)包,碰到的第一个点就是凸包上的点,这时候又可以确定两条直线,递归操作;
增量法:首先任选4个点形成的一个四面体,然后每次新加一个点;
分两种情况:1.在凸包内,无视;
2.在凸包外,找到从这个点可以“看见”的面,删除这些面,然后对于一边没有面的线段,和新加的这个点新建一个面;
至于这个点可以看见的面,就是求出这个面的方程(可以直接求法向量)。
明天就是冬令营了,{$RP++}!
code:
program poj3528; type point=record x,y,z:real end; mian=record v:array[1..4] of longint; f:point;c:real; end; var p:array[0..500] of point; s:array[0..2000] of mian; o:array[0..2000] of boolean; fa:array[0..2000] of longint; e:array[0..500,0..500] of longint; i,j,k,t,n,pt:longint; u:point;ans:real; function next(i:longint):longint; begin if fa[i]<>i then fa[i]:=next(fa[i]);next:=fa[i] end; operator -(a,b:point)c:point; begin c.x:=a.x-b.x;c.y:=a.y-b.y;c.z:=a.z-b.z end; operator +(a,b:point)c:real; begin c:=a.x*b.x+a.y*b.y+a.z*b.z end; operator *(a,b:point)c:point; begin c.z:=a.x*b.y-a.y*b.x; c.x:=a.y*b.z-a.z*b.y; c.y:=a.z*b.x-a.x*b.z; end; procedure new(i,j,k:longint);var l:longint; begin inc(t);fa[t+1]:=t+1; with s[t] do begin v[1]:=i;v[2]:=j; v[3]:=k;v[4]:=i; f:=(p[j]-p[i])*(p[k]-p[i]); c:=f+p[i]; for l:=1 to 3 do e[v[l],v[l+1]]:=t; end; end; procedure init(i,j,k,l:longint);var f:point; begin f:=(p[j]-p[i])*(p[k]-p[i]); if f+p[l]<f+p[i] then new(k,j,i) else new(i,j,k); end; begin readln(n);fa[1]:=1; for i:=1 to n do read(p[i].x,p[i].y,p[i].z); init(1,2,3,4);init(1,2,4,3); init(1,3,4,2);init(2,3,4,1); for i:=5 to n do begin j:=next(1); while j<=t do begin o[j]:=s[j].f+p[i]<s[j].c; j:=next(j+1); end; j:=next(1);pt:=t; while j<=pt do begin if o[j] then with s[j] do begin for k:=1 to 3 do if not o[e[v[k+1],v[k]]] then new(v[k],v[k+1],i); fa[j]:=j+1; end; j:=next(j+1); end; end; i:=next(1); while i<=t do begin with s[i] do begin u:=(p[v[2]]-p[v[1]])*(p[v[3]]-p[v[1]]); ans:=ans+sqrt(u.x*u.x+u.y*u.y+u.z*u.z); end; i:=next(i+1); end; writeln(ans/2:0:3); end.