Rating:1763 | 凸包: 在学凸包之前,最好把叉积弄熟! 定义: 对一个简单多边形来说,如果给定其边界上或内部的任意两个点,连接这两个点的线段上的所有点都被包含在该多边形的边界上或内部的话,则该多边形为凸多边形 。 一般的计算几何问题都是处理的离散点集形成的平面域,所以我们感兴趣的是怎样找一个包含这个点集的面积最小的凸多边形,这就是凸包。作为常识也应该知道凸包上的顶点必然是该点集的子集,所以根据此性质我们就可以设计高效算法。
而凸包算法求出的只是围成这个面的点,而并不是面积。
在这介绍下求凸包的graham算法:
预备篇 点的排序与左转判定
点的排序 首先,找一个凸包上的点,把这个点放到第一个点的位置P0。然后把P1~Pm 按照P0Pi的方向排序,可以用矢量积(叉积)判定
找给定点集的凸包,通常需要一些预处理过程,点的排序就是其中之一。下面给出一种将点按照一定规则排序的方法,这个预处理过程在很多凸包寻找算法中都扮演重要角色。 2.连结P0与其他点,分别计算这些线段与“竖直向下方向”的夹角,按照夹角由小到达的顺序将各线段的另一端(一端是P0)标号为P1、P2、P3……
举例如下: 左转判定
这是经典的计算几何学问题,判断向量p1=(x1,y1)到p2=(x2,y2)是否做左转,只需要判断x1*y2-x2*y1的正负,如果结果为正,则从p1到p2做左转。也就是向量的叉积。 (不懂的复习下,几何基础1的叉积)
Graham扫描法 基本思想:通过设置一个关于候选点的堆栈s来解决凸包问题。 操作:输入集合Q中的每一个点都被压入栈一次,非CH(Q)(表示Q的凸包)中的顶点的点最终将被弹出堆栈,当算法终止时,堆栈S中仅包含CH(Q)中的顶点,其顺序为个各顶点在边界上出现的逆时针方向排列的顺序。 注:下列过程要求|Q|>=3,它调用函数TOP(S)返回处于堆栈S 顶部的点,并调用函数NEXT-TO–TOP(S)返回处于堆栈顶部下面的那个点。但不改变堆栈的结构。 GRAHAM-SCAN(Q) 1 设P0 是Q 中Y 坐标最小的点,如果有多个这样的点则取最左边的点作为P0; 2 设<P1,P2,……,Pm>是Q 中剩余的点,对其按逆时针方向相对P0 的极角进行排序,如果有数个点有相同的极角,则去掉其余的点,只留下一个与P0 距离最远的那个点; 3 PUSH(p0 , S); 4 PUSH(p1 , S); 5 PUSH(p3 , S); 6 for i ← 3 to m 7 do while 由点NEXT-TOP-TOP(S),TOP(S)和Pi 所形成的角形成一次非左转 8 do POP(S); 9 PUSH(pi , S); 10 return S;
注:S[top]为出发点,P[i]S[top]为第一条边,S[top-1]S[top]为第二条边; 求的叉积就是P[i]S[top]*(叉积)S[top]S[top-1] 根据这个叉积判断 若大于0说明S[top]S[top-1]顺时针转能到S[top]P[i] 若小于0说明S[top]S[top-1]逆时针转能到S[top]P[i] 等于0说明两条边重合
下面给出代码: #include <iostream> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; const double eps=1e-8; const int MAX=110; struct point { double x,y; point(){} point (double _x,double _y) { x=_x,y=_y; } point operator -(const point &b)const { return point(x-b.x,y-b.y); } double operator ^(const point &b)const { return x*b.y-y*b.x; } };
struct line { point s;point e; line (){} line (point _s,point _e) { s=_s,e=_e; } };
point pxy[MAX]; point S[MAX]; double dist(point a,point b)//两点间距离 { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } bool comp(point a,point b) { if(((a-pxy[0])^(b-pxy[0]))>0) return 1; else if((((a-pxy[0])^(b-pxy[0]))==0)&&dist(a,pxy[0])<dist(b,pxy[0])) return 1; else return 0; }
void graham(int n) { int top=2; S[0]=pxy[0],S[1]=pxy[1],S[2]=pxy[2]; for(int i=3;i<n;++i) { while(((pxy[i]-S[top])^(S[top-1]-S[top]))<=0) top--; S[++top]=pxy[i]; } for(int i=0;i<=top;++i)//输出凸包点 { printf(“%lf %lf\n”,S[i].x,S[i].y); } } int main() { int n; while(scanf("%d",&n),n) { for(int i=0;i<n;++i) scanf("%lf %lf",&pxy[i].x,&pxy[i].y); point *p=&pxy[0]; for(int i=1;i<n;++i) { if(pxy[i].y<p->y) swap(pxy[i],*p); else if(pxy[i].y==p->y && pxy[i].x<p->x) swap(pxy[i],*p); } sort(pxy+1,pxy+n,comp); graham(n); } return 0; } 继续更新中…… |