凸包--Graham掃描法

一直聽大佬們說:凸包、凸包、凸包
一直不會。。。。。
而後。。。。
今天考試,考了一道計算幾何的簡單題。。。。
這,,,仍是學一下吧。。
而後考試現場學習一下凸包算法。算法

先理解一下凸包是啥東西。函數

這裏寫圖片描述

看看這張圖
解釋一下凸包是什麼
若是你有一堆點(原諒我畫的很凌亂)
那麼,找到一個點集
依次鏈接這些點
使他們造成一個凸多邊形
而且全部的點都包括在這個多邊形的內部或者邊上
這個多邊形就是一個凸包(我寫的確定一點也不嚴謹)學習

無論怎麼樣,就先這樣理解一下吧。。。。。。.net


凸包是啥應該不難理解,那麼,給你一堆點,怎麼求凸包?code

這種東西。。。。。
先大概說一下把。。。blog

首先找到最靠近左下的那個點,這個點必定在凸包上(不難理解吧。。。畫個圖就知道了)排序

以這個點爲極點,其餘點按照極角排序圖片

而後按照順序依次訪問全部點,判斷可行性get

這樣子幹說真是虛無縹緲的東西。。。。。。
畫圖來解釋class

這裏寫圖片描述

這是一片點。

這裏寫圖片描述

找到最靠近左下的一個點

這裏寫圖片描述

其餘的點按照極角排序

這裏寫圖片描述

而後把1丟到凸包的棧裏面,準備開始掃描

這裏寫圖片描述

檢查2號點是否在1的一側,(檢查一下是否是凸多邊形)
這裏檢查到2號可行,先加入到棧中

這裏寫圖片描述

檢查到3更加靠近外側(若是加入3號就會造成凹多邊形,顯然3在凸包中,而2不在)
而後把2號點彈出棧,判斷1號和3號節點的關係(同判斷2號)

這裏寫圖片描述

依次這麼判斷,最後全部凸包上的點都會在棧中

這裏寫圖片描述


這樣子算法的步驟很顯然了。
繼續解決一些細節上的問題(貌似就一個把。。。。)
怎麼計算一個節點是否在前一個點的一側。。。。
(我說的好不專業。。。我本身都不知道該怎麼說一些名詞。。。就將就着理解一下吧。。。)

咱們先拿幾個點出來

這裏寫圖片描述

其中1,2,3是當前在凸包的棧中的點,4號節點是須要判斷的點
那麼,咱們須要從棧中拿最上方的兩個點(2和3節點)
把他們鏈接起來,再把2和4鏈接起來(怎麼鏈接?我是不會說直接用向量的座標表示就能夠了)
計算一下兩個向量的叉積。。
哈,叉積。。。
解釋一下吧。。
假設2到3的向量是a(x1,y1)
2到4的向量是b(x2,y2)
那麼,計算一下它們的叉積,也就是x1y2-x2y1
換種方法來表示就是。
|a|·|b|·sin<a,b>
(因此說叉積也能夠用來求出三角形的面積~這個之後還會用到的)
若是,這兩個向量的叉積≥0 證實這兩個向量平行或者夾角是個銳角
也就證實了3號節點此時必定再也不凸包上(由於鏈接2和4以後3在凸包內側了)
把3號節點彈出棧,繼續重複上面的步驟便可。


感受我說的有點小複雜誒。。。。
這個東東多畫點圖就會理解的

若是仍是不太清楚,能夠看一看代碼。

struct Node
{
       int x,y;
}p[MAX],S[MAX];//p儲存節點的位置,S是凸包的棧 
inline bool cmp(Node a,Node b)//比較函數,對點的極角進行排序 
{
       double A=atan2((a.y-p[1].y),(a.x-p[1].x));
       double B=atan2((b.y-p[1].y),(b.x-p[1].x));
       if(A!=B)return A<B;
       else    return a.x<b.x; //這裏注意一下,若是極角相同,優先放x座標更小的點 
}
long long Cross(Node a,Node b,Node c)//計算叉積 
{
       return 1LL*(b.x-a.x)*(c.y-a.y)-1LL*(b.y-a.y)*(c.x-a.x);
}
void Get()//求出凸包 
{
       p[0]=(Node){INF,INF};int k;
       for(int i=1;i<=n;++i)//找到最靠近左下的點 
              if(p[0].y>p[i].y||(p[0].y==p[i].y&&p[i].x<p[0].x))
               {p[0]=p[i];k=i;}
       swap(p[k],p[1]);   
       sort(&p[2],&p[n+1],cmp);//對於剩餘點按照極角進行排序 
       S[0]=p[1],S[1]=p[2];top=1;//提早在棧中放入節點 
       for(int i=3;i<=n;)//枚舉其餘節點 
       {
              if(top&&Cross(S[top-1],p[i],S[top])>=0)
                        top--;//若是當前棧頂不是凸包上的節點則彈出 
              else  S[++top]=p[i++];//加入凸包的棧中 
       }
       //底下這個玩意用來輸出凸包上點的座標 
       //for(int i=0;i<=top;++i)
       //    printf("(%d,%d)\n",S[i].x,S[i].y);
}

接下來找一道簡單點的例題
HDU 1392

這道題目就是求出凸包而後計算周長,很簡單的題目,去試試吧。。

相關文章
相關標籤/搜索