## [Poj 2187] 计算几何之凸包(二) {更高效的算法}

{

(下文中所有凸包 若不做特殊说明均指点集的凸包)

}

====================================================================

 1 while true do 2     begin 3     k:=0; 4     inc(m); ch[m]:=j; 5     for i:=1 to n do 6         if (i<>j)and((k=0)or 7         cmp(p[j],p[k],p[i])) 8             then k:=i; 9     if k=temp then break;10     j:=k;11     end;

====================================================================

Graham扫描算法维护一个凸壳 通过不断在凸壳中加入新的点和去除影响凸性的点 最后形成凸包

The Graham scan is a method of computing the convex hull of a finite set of points in the plane with time complexity O(n log n). It is named after Ronald Graham, who published the original algorithm in 1972.[1] The algorithm finds all vertices of the convex hull ordered along its boundary.

http://en.wikipedia.org/wiki/Graham_scan

----------------------------------------------------------------------------------------------------------

1.点集排序

----------------------------------------------------------------------------------------------------------

2.Graham的栈扫描

Graham的扫描是一个很优美的过程 用到的数据结构也很简单 仅仅是一个栈而已

----------------------------------------------------------------------------------------------------------

3.双重共线点难题

i).排序时的共线问题

ii).扫描时的共线问题

----------------------------------------------------------------------------------------------------------

4.直角坐标排序(水平序)

i).左链和右链的旋转方向是相反的

ii).注意生成第二条链要忽略第一条链上的点

Graham扫描所用时间在点集随机时 还不如卷包裹算法快

----------------------------------------------------------------------------------------------------------

GrahamScan
{\$inline on}const    zero=1e-6; maxn=100000;type    point=record x,y:extended; end;var    p,ch:array[1..maxn]of point;    i,j,n,m,temp:longint;function sgn(x:extended):longint; inline;beginif abs(x)<zero then exit(0);if x<0 then sgn:=-1 else sgn:=1;end;function cross(a,b,c,d:point):extended; inline;begincross:=(b.x-a.x)*(d.y-c.y)-(d.x-c.x)*(b.y-a.y);end;function dist(a,b:point):extended; inline;begindist:=sqr(a.x-b.x)+sqr(a.y-b.y);end;function cmp1(a,b,c:point):boolean; inline;var    temp:longint;begintemp:=sgn(cross(a,b,a,c));if temp<>0 then exit(temp>0); {*B}cmp1:=dist(a,b)<dist(a,c);end;function cmp2(a,b,c:point):boolean; inline;var    temp:longint;begintemp:=sgn(cross(a,b,b,c));if temp<>0 then exit(temp<0); {*B}cmp2:=dist(a,b)<dist(a,c); {*A}end;procedure swap(var a,b:point); inline;var    temp:point;begintemp:=a; a:=b; b:=temp;end;procedure sort(l,r:longint);var    i,j:longint;    x:point;begini:=l; j:=r;x:=p[(l+r)>>1];repeatwhile cmp1(p[1],p[i],x) do inc(i);while cmp1(p[1],x,p[j]) do dec(j);if not(i>j)    then begin    swap(p[i],p[j]);    inc(i); dec(j);    end;until i>j;if i<r then sort(i,r);if l<j then sort(l,j);end;beginassign(input,'Hull.in'); reset(input);assign(output,'Hull2.out'); rewrite(output);readln(n);for i:=1 to n do    begin    readln(p[i].x,p[i].y);    if (p[i].x<p[1].x)or    (sgn(p[i].x-p[1].x)=0)and(p[i].y<p[1].y)        then swap(p[1],p[i]);    end;sort(2,n);while i>2 do    begin    if sgn(cross(p[1],p[i],p[1],p[i-1]))<>0        then break; dec(i);    end;temp:=(i+n+1)>>1;for j:=n downto temp do    swap(p[j],p[i+n-j]);m:=1; ch[1]:=p[1];inc(n); p[n]:=p[1];for i:=2 to n do    begin    while m>1 do        begin        if not cmp2(ch[m-1],ch[m],p[i])            then break; dec(m);        end;    inc(m); ch[m]:=p[i];    end;for i:=1 to m-1 do    writeln(ch[i].x:0:2,' ',ch[i].y:0:2);close(input); close(output);end.*A Change Direction*B Remove Colinear Points
====================================================================

------------------------------------------------------------------------------------

 1 void 快速凸包(P:点集 , S:向量 /*S.p,S.q:点)*/ ){ 2 　　/* P 在 S 左侧*/ 3 　　选取 P 中距离 S 最远的 点 Y ; 4 　　向量 A <- { S.p , Y } ; 向量 B <- { Y , S.q } ; 5 　　点集 Q <- 在 P 中 且在 A 左侧的点 ; 6 　　点集 R <- 在 P 中 且在 B 左侧的点 ; /* 划分 */ 7 　　快速凸包 ( Q , A ) ; /* 分治 */ 8 　　输出 (点 Y) ; /* 按中序输出 保证顺序*/ 9 　　快速凸包 ( P , B ) ; /* 分治 */10 }

http://www.cs.princeton.edu/courses/archive/spr10/cos226/demo/ah/QuickHull.html

QuickHull
#include <iostream>#include <math.h>#define maxn 100000#define zero 1e-12#define sgn(x) (fabs(x)<zero?0:(x>0?1:-1))#define cross(a,b,c) ((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))#define cmp(a,b) (a.x<b.x || sgn(a.x-b.x)==0 && a.y<b.y)using namespace std;struct point{    double x,y;}p[maxn];double s[maxn];void hull(int l,int r,point a,point b){    int x=l,i=l-1,j=r+1,k;    for (k=l;k<=r;k++){        double temp=sgn(s[x]-s[k]);        if (temp<0 || temp==0 && cmp(p[x],p[k])) x=k;    }    point y=p[x];    for (k=l;k<=r;k++){        s[++i]=cross(p[k],a,y);        if (sgn(s[i])>0) swap(p[i],p[k]); else i--;    }    for (k=r;k>=l;k--){        s[--j]=cross(p[k],y,b);        if (sgn(s[j])>0) swap(p[j],p[k]); else j++;    }    if (l<=i) hull(l,i,a,y);    printf("%.4lf %.4lf\n",y.x,y.y);    if (j<=r) hull(j,r,y,b);}int main(){    freopen("CH2D.in","r",stdin);    freopen("CH2D1.out","w",stdout);    int n,i,x=0;    scanf("%d",&n);    for (i=1;i<=n;i++){        scanf("%lf %lf",&p[i].x,&p[i].y);        if (x==0 || cmp(p[i],p[x])) x=i;    }    swap(p[1],p[x]);    printf("%.4lf %.4lf\n",p[1].x,p[1].y);    hull(2,n,p[1],p[1]);    return 0;}

====================================================================

(引自<算法艺术与信息学竞赛>)

====================================================================

Poj 2187 http://poj.org/problem?id=2187

====================================================================

http://westhoffswelt.de/blog/0040_quickhull_introduction_and_php_implementation.html

http://en.wikipedia.org/wiki/Graham_scan

posted on 2011-03-10 21:22 Master_Chivu 阅读(...) 评论(...) 编辑 收藏

• 随笔 - 42
• 文章 - 0
• 评论 - 123
• 引用 - 0