c++实现凸包算法
Melkman的凸包算法

/********************************************************************ac poj 1113由于1113输入已经是个简单多边形,故可以直接用melkman求凸包o(n)复杂度如果输入只是个点集合,就要先排序再用melkman 复杂度为nlgn排序可先按照x排,再y排********************************************************************/#include<stdio.h>#include<string.h>#include<math.h>#define N 1002#define pi 3.141592653589793struct POINT{int x,y;};int n,l,D[N*2],top,bot;POINT point[N];inline double dis(POINT a,POINT b){double x=a.x-b.x;double y=a.y-b.y;return sqrt(x*x+y*y);}inline int cross(POINT a,POINT b){return a.x*b.y-b.x*a.y;}int isleft(POINT o,POINT a,POINT b){ //判断ab是不是在oa的左边POINT x;POINT y;x.x=a.x-o.x;x.y=a.y-o.y;y.x=b.x-a.x;y.y=b.y-a.y;return cross(x,y);}void melkman(){int i,t;bot=n-1; top=n;D[top++]=0; D[top++]=1;for(i=2;i<n;i++){if(isleft(point[D[top-2]],point[D[top-1]],point[i])!=0) break; //寻找第三个点要保证3个点不共线!!D[top-1]=i; //共线就更换顶点}D[bot--]=i; D[top++]=i; //i是第三个点不共线!!if(isleft(point[D[n]],point[D[n+1]],point[D[n+2]])<0){ //此时队列中有3个点,要保证3个点a,b,c是成逆时针的,不是就调换abt=D[n];D[n]=D[n+1];D[n+1]=t;}for(i++;i<n;i++){if(isleft(point[D[top-2]],point[D[top-1]],point[i])>0&& //如果成立就是i在凸包内,跳过isleft(point[D[bot+1]],point[D[bot+2]],point[i])>0) continue;while(isleft(point[D[top-2]],point[D[top-1]],point[i])<=0) top--;D[top++]=i;while(isleft(point[D[bot+1]],point[D[bot+2]],point[i])<=0) bot++;D[bot--]=i;}// 凸包构造完成,D数组里bot+1至top-1内就是凸包的序列(头尾是同一点)}int main(){freopen("in.txt","r",stdin);scanf("%d%d",&n,&l);int i;for(i=0;i<n;i++) scanf("%d%d",&point[i].x,&point[i].y);melkman();double ans=0;for(i=bot+1;i<top-1;i++) ans+=dis(point[D[i]],point[D[i+1]]);ans+=2*pi*l;printf("%.0f\n",ans);return 0;}。
算法——蛮力法之最近对问题和凸包问题

算法——蛮⼒法之最近对问题和凸包问题 上次的博客写到⼀半宿舍停电了。
然⽽今天想起来补充完的时候发现博客园并没有⾃动保存哦,微笑。
最近对问题 ⾸先来看最近对问题,最近对问题描述的就是在包含n个端的集合中找到距离最近的两个点,当然问题也可以定义在多维空间中,但是这⾥只是跟随书上的思路实现了⼆维情况下的最近对问题。
假设所有讨论的点是以标准的笛卡尔坐标形式(x,y)给出的,那么在两个点P i=(X i,Y i)和P j=(X j,Y j)之间的距离是标准的欧⼏⾥得距离: d(P i,P j)=sqrt( (X1-X2)2+(Y1-Y2)2 )蛮⼒法的思路就是计算出所有的点之间的距离,然后找出距离最⼩的那⼀对,在这⾥增加效率的⼀种⽅式是只计算点下标 i<j 的那些对点之间的距离,这样就避免了重复计算同⼀对点间距离。
下⾯是蛮⼒法解决最近对问题的算法:使⽤蛮⼒法求平⾯中距离最近的两点BruteForceClosetPoints(P)//输⼊:⼀个n(n≥2)的点的列表P,P i=(X i,Y i)//输出:距离最近的两个点的下标index1和index2dmin <— ∞for i <— 1 to n-1 do for j <— i+1 to n do d <— sqrt( (X i-X i)2+(Y j-Y j)2 ) if d<dmin dmin=d; index1=i; index2=j;return index1,index2 该算法的关键步骤是基本操作虽然是计算两个点之间的欧⼏⾥得距离,但是求平⽅根并不是像加法乘法那么简单。
上⾯算法中,开平⽅函数导数恒⼤于0,它是严格递增的,因此我们可以直接只计算(X i-X i)2+(Y j-Y j)2,⽐较d2的⼤⼩关系,这样基本操作就变成了求平⽅。
平⽅操作的执⾏次数是: n(n-1)∈Θ(n2)因此,蛮⼒法解决最近对问题的平均时间复杂度是Θ(n2) 下⾯是该算法的c++代码实现部分,在实现这个算法时,我碰到了三个问题: ⼀是:怎么表⽰⼀个点集,因为最终返回的下标是集合中点的下标,要⽤的数据结构就是⼀维数组,但是点的xy坐标⼜要怎么表⽰呢,这⾥我在头⽂件中创建了struct类型的点结构,该结构拥有的成员变量就是x代表的横坐标和y代表的纵坐标,这样就可以直接创建该结构的⼀位数组进⾏计算了。
OpenCV 3.1.0 图像处理教程-29凸包

代码演示
首先把图像从RGB转为灰度 然后再转为二值图像 在通过发现轮廓得到候选点 凸包API调用 绘制显示。
演示penCV 3.1.0 – 图像处理教程
凸包-Convex Hull
概念介绍 API说明 代码演示
概念介绍
什么是凸包(Convex Hull),在一个多变形边缘或者内部任 意两个点的连线都包含在多边形边界或者内部。 正式定义: 包含点集合S中所有点的最小凸多边形称为凸包
检测算法 - Graham扫描法
概念介绍-Graham扫描算法
No worry,我们只是需要了解, OpenCV已经实现了凸包发现 算法和API提供我们使用。
API说明cv::convexHull
convexHull( InputArray points,// 输入候选点,来自findContours OutputArray hull,// 凸包 bool clockwise,// default true, 顺时针方向 bool returnPoints)// true 表示返回点个数,如果第二个参数是
概念介绍-Graham扫描算法
首先选择Y方向最低的点作为起始点p0 从p0开始极坐标扫描,依次添加p1….pn(排序顺序是
根据极坐标的角度大小,逆时针方向) 对每个点pi来说,如果添加pi点到凸包中导致一个左转
向(逆时针方法)则添加该点到凸包, 反之如果导致 一个右转向(顺时针方向)删除该点从凸包中
Graham算法求凸包完整程序代码

#include <iostream>#include <cmath>#include <windows.h>using namespace std;/*PointSet[]:输入的点集ch[]:输出的凸包上的点集,按照逆时针方向排列n:PointSet中的点的数目len:输出的凸包上的点的个数*/struct Point{float x,y;};//小于,说明向量p0p1的极角大于p0p2的极角float multiply(Point p1,Point p2,Point p0){return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));}float dis(Point p1,Point p2){return(sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));}void Graham_scan(Point PointSet[],Point ch[],int n,int &len){int i,j,k=0,top=2;Point tmp;//找到最下且偏左的那个点for(i=1;i<n;i++)if((PointSet[i].y<PointSet[k].y)||((PointSet[i].y==PointSet[k].y)&&(PointSet[i].x<Point Set[k].x)))k=i;//将这个点指定为PointSet[0]tmp=PointSet[0];PointSet[0]=PointSet[k];PointSet[k]=tmp;//按极角从小到大,距离偏短进行排序for (i=1;i<n-1;i++){k=i;for (j=i+1;j<n;j++)if( (multiply(PointSet[j],PointSet[k],PointSet[0])>0)||((multiply(PointSet[j],PointSet[k],PointSet[0])==0)&&(dis(PointSet[0],PointSet[j])<dis(PointSet[0],PointSet[k]))) )k=j;//k保存极角最小的那个点,或者相同距离原点最近tmp=PointSet[i];PointSet[i]=PointSet[k];PointSet[k]=tmp;}//第三个点先入栈ch[0]=PointSet[0];ch[1]=PointSet[1];ch[2]=PointSet[2];//判断与其余所有点的关系for (i=3;i<n;i++){//不满足向左转的关系,栈顶元素出栈while(multiply(PointSet[i],ch[top],ch[top-1])>=0) top--;//当前点与栈内所有点满足向左关系,因此入栈.ch[++top]=PointSet[i];}len=top+1;}const int maxN=1000;Point PointSet[maxN];Point ch[maxN];int n;int len;int main(){HDC hdc = GetWindowDC( GetDesktopWindow() ); //用桌面作为画图背景HPEN hpen1 = CreatePen( PS_SOLID, 1, RGB(255,0,0) );//画笔是像素为1的红色直线HPEN hpen_old = (HPEN)SelectObject( hdc, hpen1 ); //选择设置的画笔int n=80;for(int i=0;i<n;i++){PointSet[i].x=rand()%200+200;PointSet[i].y=rand()%200+200;SetPixel( hdc, PointSet[i].x, PointSet[i].y , RGB(0, 255, 0) );}Graham_scan(PointSet,ch,n,len);for(i=0; i<len-1; i++){MoveToEx( hdc, ch[i].x, ch[i].y, NULL);LineTo( hdc, ch[i+1].x, ch[i+1].y );}MoveToEx( hdc, ch[0].x, ch[0].y, NULL);LineTo( hdc, ch[i].x, ch[i].y );return 0;}。
凸包算法-判断凸多边形是否相交

/*凸包算法判断凸多边形是否相交*/#include <stdio.h>#include <stdlib.h>#include <math.h>#define MaxNode 100015#define INF 99999999int stack[MaxNode];int top;typedef struct TPoint{double x;double y;}TPoint;TPoint black[2005], white[2005];TPoint p0;typedef struct TPloygon{TPoint p[2005];int n;}TPolygon;TPolygon covtexb, covtexw;typedef struct TSegment{TPoint p1;TPoint p2;}TSegment;void swap(TPoint point[], int i, int j) {TPoint tmp;tmp = point[i];point[i] = point[j];point[j] = tmp;}double max(double x, double y){//比较两个数的大小,返回大的数if(x > y) return x;else return y;}double min(double x, double y){//比较两个数的大小,返回小的数if(x < y) return x;else return y;}double multi(TPoint p1, TPoint p2, TPoint p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}double distanc(TPoint p1, TPoint p2){return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); }int cmp(const void *a, const void *b){TPoint *c = (TPoint *)a;TPoint *d = (TPoint *)b;double k = multi(*c, *d, p0);if(k < 0) return 1;else if(k == 0 && distanc(*c, p0) >= distanc(*d, p0)) return 1;else return -1;}void grahamScan(TPoint point[], int n){//Graham扫描求凸包int i, u;//将最左下的点调整到p[0]的位置u = 0;for(i = 1;i <= n - 1;i++){if((point[i].y < point[u].y) ||(point[i].y == point[u].y && point[i].x < point[u].x))u = i;}swap(point, 0, u);p0 = point[0];//将平p[1]到p[n - 1]按按极角排序,可采用快速排序qsort(point + 1, n - 1, sizeof(point[0]), cmp);for(i = 0;i <= 2;i++) stack[i] = i;top = 2;for(i = 3;i <= n - 1;i++){while(multi(point[i], point[stack[top]], point[stack[top - 1]]) >= 0){ if(top == 0)break;top--;}top++;stack[top] = i;}}bool isIntersected(TPoint s1, TPoint e1, TPoint s2, TPoint e2){//判断线段是否相交//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交//2.跨立试验if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0)) return true;return false;}bool Intersect(TSegment L1, TSegment L2){//线段l1与l2相交而且不在端点上时,返回true//判断线段是否相交//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交TPoint s1 = L1.p1;TPoint e1 = L1.p2;TPoint s2 = L2.p1;TPoint e2 = L2.p2;//2.跨立试验if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0)) return true;return false;}bool Online(TSegment L, TPoint p){//p在L上(不在端点)时返回true//1.在L所在的直线上//2.在L为对角线的矩形中double dx, dy, dx1, dy1;dx = L.p2.x - L.p1.x;dy = L.p2.y - L.p1.y;dx1 = p.x - L.p1.x;dy1 = p.y - L.p1.y;if(dx * dy1 - dy * dx1 != 0) return false; //叉积if(dx1 * (dx1 - dx) < 0 || dy1 * (dy1 - dy) < 0) return true;return false;}bool same1(TSegment L, TPoint p1, TPoint p2){//判断p1, p2是否在L的同侧if(multi(p1, L.p2, L.p1) * multi(L.p2, p2, L.p1)< 0) return true;return false;bool Inside(TPoint q, TPolygon polygon){int c, i, n;TSegment L1, L2;c = 0;n = polygon.n;L1.p1 = q;L1.p2 = q;L1.p2.x = INF;/*(1)相交1.p[i]和p[i+1]在L的两侧2.p[i]和p[i+2]在L的同侧3.p[u]和p[i+3]在L的同侧或异侧*/for(i = 0;i < polygon.n;i++){L2.p1 = polygon.p[i];L2.p2 = polygon.p[(i + 1) % n];if(Intersect(L1, L2)){c++;continue;}if(!Online(L1, polygon.p[(i + 1) % n])) continue;if(!Online(L1, polygon.p[(i + 2) % n]) &&!same1(L1, polygon.p[i], polygon.p[(i + 2) % n])){c++;continue;}if(Online(L1, polygon.p[(i + 2) % n]) &&!same1(L1, polygon.p[i], polygon.p[(i + 3) % n]))c++;}if(c % 2 == 0) return false;else return true;}int covtexintet(){int i, j, s, t;for(i = 0;i < covtexb.n;i++){for(j =i + 1;j < covtexb.n;j++){for(s = 0;s < covtexw.n;s++){for(t = s + 1;t < covtexw.n;t++){if(isIntersected(covtexb.p[i], covtexb.p[j], covtexw.p[s],covtexw.p[t])) return 0;}}}}if(Inside(covtexb.p[covtexb.n - 1], covtexw) || Inside(covtexw.p[covtexw.n - 1], covtexb)) return 0;else return 1;}int main(){int i, d, t1, t2, p, test = 1;double x1, y1, x2, y2;while(scanf("%d%d", &d, &p) && d && p){if(test != 1) printf("\n");t1 = 0;for(i = 0;i < d;i++){scanf("%lf%lf%lf%lf",&x1, &y1, &x2, &y2);black[t1].x = x1;black[t1].y = y1;t1++;black[t1].x = x2;black[t1].y = y2;t1++;black[t1].x = x1;black[t1].y = y2;t1++;black[t1].x = x2;black[t1].y = y1;}t2 = 0;for(i = 0;i < p;i++){scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);white[t2].x = x1;white[t2].y = y1;t2++;white[t2].x = x2;white[t2].y = y2;t2++;white[t2].x = x1;white[t2].y = y2;t2++;white[t2].x = x2;white[t2].y = y1;}grahamScan(black, t1 + 1);for(i = 0;i <= top;i++){covtexb.p[i].x = black[stack[i]].x;covtexb.p[i].y = black[stack[i]].y;}covtexb.n = top + 1;grahamScan(white, t2 + 1);for(i = 0;i <= top;i++){covtexw.p[i].x = white[stack[i]].x;covtexw.p[i].y = white[stack[i]].y;}covtexw.n = top + 1;if(covtexintet())printf("Case %d: It is possible to separate the two groups of vendors.\n", test++);elseprintf("Case %d: It is not possible to separate the two groups of vendors.\n", test++); }return 0;。
最小凸包算法

最⼩凸包算法使⽤Graham扫描法进新解决最⼩凸包问题先找到最左下端点然后根据极⾓来进⾏逆时针排序在根据相对极⾓增减来去除不需要的点C++代码1 #include<iostream>2 #include<cstdio>3 #include<cstring>4 #include<algorithm>5 #include<cmath>6#define PI 3.14159265357using namespace std;8struct node9 {10int x,y;11 };12 node vex[1000];//存⼊的所有的点13 node stackk[1000];//凸包中所有的点14int xx,yy;15bool cmp1(node a,node b)//排序找第⼀个点16 {17if(a.y==b.y)18return a.x<b.x;19else20return a.y<b.y;21 }22int cross(node a,node b,node c)//计算叉积23 {24return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);25 }26double dis(node a,node b)//计算距离27 {28return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y));29 }30bool cmp2(node a,node b)//极⾓排序另⼀种⽅法,速度快31 {32if(atan2(a.y-yy,a.x-xx)!=atan2(b.y-yy,b.x-xx))33return (atan2(a.y-yy,a.x-xx))<(atan2(b.y-yy,b.x-xx));34return a.x<b.x;35 }36bool cmp(node a,node b)//极⾓排序37 {38int m=cross(vex[0],a,b);39if(m>0)40return1;41else if(m==0&&dis(vex[0],a)-dis(vex[0],b)<=0)42return1;43else return0;44/*if(m==0)45 return dis(vex[0],a)-dis(vex[0],b)<=0?true:false;46 else47 return m>0?true:false;*/48 }49int main()50 {51int t,L;52while(~scanf("%d",&t),t)53 {54int i;55for(i=0; i<t; i++)56 {57 scanf("%d%d",&vex[i].x,&vex[i].y);58 }59if(t==1)60 printf("%.2f\n",0.00);61else if(t==2)62 printf("%.2f\n",dis(vex[0],vex[1]));63else64 {65 memset(stackk,0,sizeof(stackk));66 sort(vex,vex+t,cmp1);67 stackk[0]=vex[0];68 xx=stackk[0].x;69 yy=stackk[0].y;70 sort(vex+1,vex+t,cmp2);//cmp2是更快的,cmp更容易理解71 stackk[1]=vex[1];//将凸包中的第两个点存⼊凸包的结构体中72int top=1;//最后凸包中拥有点的个数73for(i=2; i<t; i++)74 {75while(i>=1&&cross(stackk[top-1],stackk[top],vex[i])<0) //对使⽤极⾓排序的i>=1有时可以不⽤,但加上总是好的76 top--;77 stackk[++top]=vex[i]; //控制<0或<=0可以控制重点,共线的,具体视题⽬⽽定。
OpenCV学习(29)凸包(convexhull)

OpenCV学习(29)凸包(convexhull)在opencv中,通过函数convexHull l能很容易的得到⼀系列点的凸包,⽐如由点组成的轮廓,通过convexHull函数,我们就能得到轮廓的凸包。
下⾯的图就是⼀些点集的凸包。
求凸包的代码如下:int main( int /*argc*/, char** /*argv*/ ){Mat img(500, 500, CV_8UC3);RNG& rng = theRNG();cout << "\n这个程序演⽰了凸包函数的使⽤,任意给定⼀些点,求出包围这些点的凸包\n" <<endl;for(;;){char key;int i, count = (unsigned)rng%100 + 1;vector<Point> points;//随机在1-100个点,这些点位于图像中⼼3/4处。
for( i = 0; i < count; i++ ){Point pt;pt.x = rng.uniform(img.cols/4, img.cols*3/4);pt.y = rng.uniform(img.rows/4, img.rows*3/4);points.push_back(pt);}//计算凸包vector<int> hull;convexHull(Mat(points), hull, true);//画随即点img = Scalar::all(0);for( i = 0; i < count; i++ )circle(img, points[i], 3, Scalar(0, 0, 255), CV_FILLED, CV_AA);int hullcount = (int)hull.size();Point pt0 = points[hull[hullcount-1]];//画凸包for( i = 0; i < hullcount; i++ ){Point pt = points[hull[i]];line(img, pt0, pt, Scalar(0, 255, 0), 1, CV_AA);pt0 = pt;}imshow("hull", img);key = (char)waitKey();if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'break;}return 0;}convexHull(Mat(points), hull, true);convexconvexHull第⼀个参数是要求凸包的点集,第⼆个参数是输出的凸包点,第三个参数是⼀个bool变量,表⽰求得的凸包是顺时针⽅向还是逆时针⽅向,true是顺时针⽅向。
凸包算法

课程名称:空间分析指导老师:李斌班号:学号:姓名:凸包的建立算法地测学院杨博1.凸包:凸包概念:点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。
下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。
一组平面上的点,求一个包含所有点的最小的凸多边形,这就是凸包问题了。
这可以形象地想成这样:在地上放置一些不可移动的木桩,用一根绳子把他们尽量紧地圈起来,这就是凸包了。
凸包可以使所有点在一定范围内,便于空间分析。
2.算法框图:算法基本过程:(1)鼠标输入点,并将各点坐标记录到数组p[]中(2)寻找y坐标最小点p[i],并与p[0]互换坐标值(这样保证了画凸包点从最下面点开始,且点位不丢失)(3)分别将p[0],p[1]赋给c[0],c[1](数组c[]存放凸包点坐标)(4)利用向量公式R=(c[t-1]-p[i])*(c[t]-p[i])计算R值,以此来判断左右旋如下图是右旋,若右旋或共线c[++t]=p[i],i++,若为左旋c[t]=p[i],i++。
(r>0:p1在矢量p2p0的逆时针方向;r=0:p0p2p1三点共线;r<0:p1在矢量p2p0的顺时针方向,即r>0 左旋, <0 右旋, =0共线)(5)i<n时(n为输入点数)重复(3)到(4)操作(6)重复调用画线程序连线c[i]到c[i-1]直到i<t。
(7)完成凸包绘图3.算法设计与实现过程算法实现的语言平台VC++4. 算法所用数据及计算结果点由鼠标点击输入,计算结果如下图:算法易懂,效率较高,可清除后重画5.算法源代码、注释及使用说明#include <iostream.h>#include <time.h>#include <math.h>#include <stdio.h>#include <stdlib.h>#define eps 1e-9/*class Point{public:void set(double ix,double iy){x=ix;y=iy;}double xOffset(){return x;}double yOffset(){return y;}double angle(){angl=atan2(y,x);return angl;}protected:double x;double y;double angl;};*/struct Point{double x;double y;};double multiply(Point p1,Point p2,Point p0){return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));};double distance(Point p1,Point p2){return(sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y))); };/*pointset为输入的点集;ch为输出的凸包上的点集,按照逆时针方向排列;n为pointset中的点的数目len为输出的凸包上的点的个数*/void Graham(Point pointset[],Point ch[],int n,int &len){int i,j,k=0,top=2;Point tmp;/*选取pointset中y坐标最小的点pointset[k],如果这样的点有多个,则取最左边的一个*/for(i=1;i<n;i++)if((pointset[i].y<pointset[k].y)||((pointset[i].y==pointset[k].y)&& (pointset[i].x<pointset[k].x)))k=i;tmp=pointset[0];pointset[0]=pointset[k];pointset[k]=tmp; /*现在pointset中y坐标最小的点在pointset[0]*/ for (i=1;i<n-1;i++) /*对顶点按照相对pointset[0]的极角从小到大进行排序,极角相同的按照距离pointset[0]从近到远进行排序*/{k=i;for (j=i+1;j<n;j++)if ( (multiply(pointset[j],pointset[k],pointset[0])>0)||(( fabs(multiply(pointset[j],pointset[k],pointset[0]))<eps)&&(distance(pointset[0],pointset[j])<distance(pointset[0],pointse t[k])) ))k=j;tmp=pointset[i];pointset[i]=pointset[k];pointset[k]=tmp;}ch[0]=pointset[0];ch[1]=pointset[1];ch[2]=pointset[2];for (i=3;i<n;i++){while (top>0 && multiply(pointset[i],ch[top],ch[top-1])>=0) top--;ch[++top]=pointset[i];}len=top+1;};void main(){//strand(time(0));int n;cout<<"please input a number: ";cin>>n;Point p[3];}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//poj3528,裸的三维凸包,可做三维凸包的模板
判断点p是否能“看见”面abc的方法是:求(pa叉乘pb)点乘pc,如果这个结果大于0,则p能“看到”面abc。
(必须保证从凸包外看去,每个构成凸包的面上的点的顺序为右手系) 判断是否为临界棱的方法:从点p能“看见”与该棱相邻的一个面,而不能“看见”与它相邻的另一个面,则该棱为临界棱。
判断点p是否在凸包内部:若从p看不到任何凸包上的面,则p在凸包内部,忽略。
#include<iostream>
using namespace std;
#include<cmath>
#include<cstring>
#include<cstdio>
#define N 505
#define eps 0.000001
struct Point
{
double x,y,z;
Point(){}
Point(double _x,double _y,double _z)
{
x=_x;
y=_y;
z=_z;
}
Point operator-(Point t1)//向量减法
{
return Point(x-t1.x,y-t1.y,z-t1.z);
}
Point operator*(Point t2)//叉积
{
return Point(y*t2.z-t2.y*z,z*t2.x-x*t2.z,x*t2.y-y*t2.x);
}
double operator^(Point t3)//点积
{
return x*t3.x+y*t3.y+z*t3.z;
}
};
struct Plane
{
int a,b,c;//a,b,c为三个点的编号,a,b,c要满足从凸包外面看成右手系
bool in;//表示该平面是否在凸包内
};
void Swap(Point &a,Point &b)
{
Point c;
c=a;
a=b;
b=c;
}
Point point[N];
Plane plane[N*10];
int edge[N][N];
int plen;//计算过的面的个数
void dfs(int p,int t);
double vol(Point p,Plane f)//p与平面abc组成的四面体的有向体积的倍
{
Point a=point[f.a]-p,b=point[f.b]-p,c=point[f.c]-p;
return (a*b)^c;
}
double vlen(Point a)//求向量a的模
{
return sqrt(a.x*a.x+a.y*a.y+a.z*a.z);
}
void deal(int p,int t1,int t2)
{
int t=edge[t1][t2];//搜索与该边相邻的另外一个平面
if(plane[t].in)
{
if(vol(point[p],plane[t])>eps)
dfs(p,t);
else
{
Plane add;
add.a=t2,add.b=t1,add.c=p,add.in=true;//这里注意顺序,就可以不用Swap了,add.a,add.b,add.c要成右手系
edge[add.a][add.b]=edge[add.b][add.c]=edge[add.c][add.a]=plen;
plane[plen++]=add;
}
}
}
void dfs(int p,int t)//递归搜索所有应该从凸包内删除的面
{
plane[t].in=false;
deal(p,plane[t].b,plane[t].a);//注意,a和b的顺序刚好跟下面的相反,为的就是搜索与边(point[plane[t].a],point[plane[t].b])相邻的另外一个平面
deal(p,plane[t].c,plane[t].b);
deal(p,plane[t].a,plane[t].c);
}
int del(int n)//增量法,有n个点,返回计算过的平面个数,若无法构成凸包,则返回-1 {
if(n<4)//如果点数小于,则无法构成凸包,若已保证点数大于或等于,可略去return -1;
/******************这一段用来保证前四点不共面,若已保证,可略去bool allTheSamePoint=true;
for(int i=1;i<n;i++)//保证前两点不共点
{
if(vlen(point[i]-point[0])>eps)
{
Swap(point[1],point[i]);
allTheSamePoint=false;
break;
}
}
if(allTheSamePoint)
return -1;
bool allTheSameLine=true;
for(int i=2;i<n;i++)//保证前三点不共线
{
if(vlen((point[1]-point[0])*(point[i]-point[1]))>eps)
{
Swap(point[2],point[i]);
allTheSameLine=false;
break;
}
}
if(allTheSameLine)
return -1;
bool allTheSamePlane=true;
for(int i=3;i<n;i++)//保证前四点不共面
{
if(fabs((point[1]-point[0])*(point[2]-point[0])^(point[i]-point[0]))>eps)
{
Swap(point[3],point[i]);
allTheSamePlane=false;
break;
}
}
if(allTheSamePlane)
return -1;
这一段用来保证前四点不共面,若已保证,可略去************/
plen=0;//计算过的面的个数
Plane add;
for(int i=0;i<4;i++)
{
add.a=(i+1)%4,add.b=(i+2)%4,add.c=(i+3)%4,add.in=true;
if(vol(point[i],add)>0)
swap(add.a,add.b);
edge[add.a][add.b]=edge[add.b][add.c]=edge[add.c][add.a]=plen;//记录与该边相邻的其中一个面,并且该顺序在该面内(从凸包外看)成右手系,因此,该面是唯一的plane[plen++]=add;
}
for(int i=4;i<n;i++)
{
for(int j=0;j<plen;j++)
{
if(plane[j].in && vol(point[i],plane[j])>eps)
{
dfs(i,j);
break;
}
}
}
return plen;
}
double area(Plane a)
{
return vlen((point[a.b]-point[a.a])*(point[a.c]-point[a.a]))/2.0;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>point[i].x>>point[i].y>>point[i].z;
int len=del(n);
if(len==-1)
printf("0.000\n");
else
{
double ans=0.0;
for(int i=0;i<len;i++)
{
if(plane[i].in)
{
ans+=area(plane[i]);
}
}
printf("%.3lf\n",ans);
}
return 0;
}。