二维凸包基本知识

来源:互联网 发布:centos强制删除文件夹 编辑:程序博客网 时间:2024/05/16 10:15

关于凸包问题大致上分为以下步骤:

   (1) 点的排序

             找给定点集的凸包,通常需要一些预处理过程,点的排序就是其中之一。
             下面给出一种将点按照一定规则排序的方法,这个预处理过程在很多凸包寻找算法中都扮演重要角色
              1.找一个必在凸包上的点(通常取横坐标或纵坐标最小的点)
                         记为P0,如图:
                                                                   
                                                                                     
              2.连结P0与其他点,分别计算这些线段与“竖直向下方向”的夹角,按照夹角
                        由小到达的顺序将各线段的另一端(一端是P0)标号为P1、P2、P3……如图:
                                                                 
                                      
                                                                                                (为了使图像更清晰,现擦去所有线段)


    (2)左转判断       

          这是经典的计算几何学问题,判断向量p1=(x1,y1)到p2=(x2,y2)是否做左转,只需要判断x1*y2-x2*y1的正负,如果结果为正,则从p1到p2做左转。
               矢量叉积
               设有点p0(x0,y0),p1(x1,y1),p2(x2,y2).(p0p1),(p0p2)是共p0的两条向量,
               叉积d = (p0p1)x(p0p2) = (x1-x0)*(y2-y0) -(x2-x0)*(y1-y0)
                           叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系:
                           若 d> 0 , 则(p0p1)在(p0p2)的顺时针方向。 
                           若 d< 0 , 则(p0p1)在(p0p2)的逆时针方向。(图示方向)
                           若 d = 0 , 则(p0p1)与(p0p2)共线,但可能同向也可能反向                                                                    
       
相关博客http://blog.csdn.net/fivedoumi/article/details/7653128



Graham'sScan法求解凸包问题
基本步骤:

⒈在所有点中选取y坐标最小的一点P0。然后按照其它各点p和基点构成的向量<H,p>;与x轴的夹角进行排序,夹角由大至小进行顺时针扫描,反之则进行逆时针扫描。实现中无需求得夹角,只需根据向量的内积公式求出向量的模即可。


2.当加入一点时,必须考虑到前面的线段是否会出现在凸包上。从基点开始,凸包上每条相临的线段的旋转方向应该一致,并与扫描的方向相反。如果发现新加的点使得新线段与上线段的旋转方向发生变化,则可判定上一点必然不在凸包上。实现时可用向量叉积进行判断,叉积为正(逆时针扫描判断是否为负),则将上一点删除。删除过程需要回溯,将之前所有叉积符号相反的点都删除,然后将新点加入凸包。

按照上述步骤进行扫描,直到点集中所有的点都遍例完成,即得到凸包。

代码如下:

#include<stdio.h>#include<math.h>#include<algorithm>#include<iostream>using namespace std;const int MAXN=1000;struct point{    int x,y;};point list[MAXN];int stack[MAXN],top;int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2 {    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);}    double dis(point p1,point p2)  //计算 p1p2的 距离 {    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));}    bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面 {    int tmp=cross(list[0],p1,p2);    if(tmp>0) return true;    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;    else return false;}    void init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序 {    int i,k;    point p0;    scanf("%d%d",&list[0].x,&list[0].y);    p0.x=list[0].x;    p0.y=list[0].y;    k=0;    for(i=1;i<n;i++)    {        scanf("%d%d",&list[i].x,&list[i].y);        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )        {            p0.x=list[i].x;            p0.y=list[i].y;            k=i;        }        }        list[k]=list[0];    list[0]=p0;        sort(list+1,list+n,cmp);}     void graham(int n){    int i;    if(n==1)    {top=0;stack[0]=0;}    if(n==2)    {        top=1;        stack[0]=0;        stack[1]=1;    }        if(n>2)    {        for(i=0;i<=1;i++) stack[i]=i;        top=1;                for(i=2;i<n;i++)        {            while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0)             top--;            top++;            stack[top]=i;        }        }    }
原文来自:<a target=_blank href="http://m.blog.csdn.net/blog/u010468553/9385537">http://m.blog.csdn.net/blog/u010468553/9385537</a>
0 0