凸包-Andrew演算法&&Graham掃描法
凸包簡介:
在二維平面上(二維凸包)給出若干個點,能夠包含這若干個點的面積最小的凸多邊形稱為凸包(可以想像有很多個釘子釘在牆上,然後用一個橡皮圈套在所有的釘子上,最後橡皮圈形成的就是一個凸包)。
Graham掃描法:
Graham掃描法是一種基於極角排序的進行求解的演算法,其大致流程如下:
①找一個一定在凸包上的點P0(一般找縱座標最小的點);
②將其餘所有的點以P0為基準進行極角排序;
③從P0出發掃描所有的點,不斷地更新最外圍的點,是否在最外圍可由叉乘判斷。這裡用個圖說明一下:
當前對點P進行判斷,P1,P2為前面加入的兩個點:
Ⅰ)若點P在內部(圖一),則有向量b叉乘向量a小於零,此時點P是凸包的頂點,應將P點加入凸包。
Ⅱ)若在點P在外部,則有向量b叉乘向量a大於零,此時應該將點P1捨棄,繼續對前兩個點進行判斷,直到P、P1、P2三個點滿足向量b叉乘向量a小於零,再將P點加入凸包。
當然網上面還有更詳細的例子,不懂得話可以再看看其餘的資料( ̄▽ ̄)。
具體程式碼如下:
//Graham掃描法-hdu1392 #include<iostream> #include<cstring> #include<string> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=1e6+10; const double esp=1e-10; struct Point{ double x,y; Point(){} Point(double _x,double _y){ x=_x;y=_y; } }P[maxn],Convexhull[maxn]; typedef Point Vector; Vector operator + (Vector A,Vector B){ return Vector(A.x+B.x,A.y+B.y); } Vector operator - (Vector A,Vector B){ return Vector(A.x-B.x,A.y-B.y); } Vector operator * (Vector A,double d){ return Vector(A.x*d,A.y*d); } Vector operator / (Vector A,double d){ return Vector(A.x/d,A.y/d); } double Dot(Vector A,Vector B){ return A.x*B.x+A.y*B.y; } double Cross(Vector A,Vector B){ return A.x*B.y-A.y*B.x; } double Length(Vector A){ return sqrt(Dot(A,A)); } int dcmp(double x){ return fabs(x)<esp?0:x<0?-1:1; } bool Angle_Cmp(Point p1,Point p2){ double res=Cross(p1-P[0],p2-P[0]); return dcmp(res)>0||(dcmp(res)==0&&dcmp(Length(p1-P[0])-Length(p2-P[0]))<0); } int Graham(int n){ int k=0; Point p0=P[0]; for(int i=1;i<n;i++){ if(p0.y>P[i].y||(p0.y==P[i].y&&p0.x>P[i].x)){ k=i;p0=P[i]; } } swap(P[0],P[k]); sort(P+1,P+n,Angle_Cmp); Convexhull[0]=P[0]; //Assume n>2 Convexhull[1]=P[1]; int top=2; for(int i=2;i<n;i++){ while(top>1&&Cross(P[i]-Convexhull[top-2],Convexhull[top-1]-Convexhull[top-2])>=0)top--; Convexhull[top++]=P[i]; } return top; } int main(){ freopen("in.txt","r",stdin); int n; while(~scanf("%d",&n)&&n){ for(int i=0;i<n;i++)scanf("%lf%lf",&P[i].x,&P[i].y); int cnt=Graham(n); double ans=0; if(cnt!=2)ans+=Length(Convexhull[0]-Convexhull[cnt-1]); for(int i=0;i<cnt-1;i++)ans+=Length(Convexhull[i]-Convexhull[i+1]); printf("%.2lf\n",ans); } return 0; }
Andrew演算法:
Andrew演算法是一種基於水平序的演算法,在許多的資料上都會發現說該演算法可以看作Graham掃描法的一種變體,為什麼這麼說呢?我的理解就是二者都是對所有的點進行掃描得到凸包,不過掃描之前做的處理不同,Andrew演算法的大致流程如下:
①將所有的點按照橫座標從小到大進行排序,橫座標相同則按縱座標從小到大排;
②將P[0]和P[1]加入凸包,然後從P[2]開始判斷,判斷方式同Graham演算法中的判斷一致;
③將所有的點掃描一遍以後,我們便可以得到一個“下凸包”(為什麼?畫個圖就懂了--橫座標不會減小);
④同理,我們從P[n-2]開始(P[n-1]已經判過了),反著掃描一遍,便可以得到一個“上凸包”;
⑤將兩個“半凸包”合在一起就是一個完整的凸包,注意的是由於起點P[0]在正著掃描和反著掃描時都會將其加入凸包,故需要將最後一個點(P[0])去掉才為最終結果。
具體程式碼如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
const double esp=1e-10;
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x=_x;y=_y;
}
}P[maxn],Convexhull[maxn];
typedef Point Vector;
Vector operator + (Vector A,Vector B){
return Vector(A.x+B.x,A.y+B.y);
}
Vector operator - (Vector A,Vector B){
return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double d){
return Vector(A.x*d,A.y*d);
}
Vector operator / (Vector A,double d){
return Vector(A.x/d,A.y/d);
}
double Dot(Vector A,Vector B){
return A.x*B.x+A.y*B.y;
}
double Cross(Vector A,Vector B){
return A.x*B.y-A.y*B.x;
}
double Length(Vector A){
return sqrt(Dot(A,A));
}
int dcmp(double x){
return fabs(x)<esp?0:x<0?-1:1;
}
bool operator <(Point p1,Point p2){
return dcmp(p1.x-p2.x)<0||(dcmp(p1.x-p2.x)==0&&dcmp(p1.y-p2.y)<0);
}
int Andrew(int n){ //Assume n>2
sort(P,P+n);
int top=0;
for(int i=0;i<n;i++){
while(top>1&&dcmp(Cross(P[i]-Convexhull[top-2],Convexhull[top-1]-Convexhull[top-2]))>=0)top--;
Convexhull[top++]=P[i];
}
int k=top;
for(int i=n-2;i>=0;i--){
while(top>k&&dcmp(Cross(P[i]-Convexhull[top-2],Convexhull[top-1]-Convexhull[top-2]))>=0)top--;
Convexhull[top++]=P[i];
}
return top-1;
}
int main(){
freopen("in.txt","r",stdin);
int n;
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;i++)scanf("%lf%lf",&P[i].x,&P[i].y);
int cnt=Andrew(n);
double ans=0;
if(cnt!=2)ans+=Length(Convexhull[0]-Convexhull[cnt-1]);
for(int i=0;i<cnt-1;i++)ans+=Length(Convexhull[i]-Convexhull[i+1]);
printf("%.2lf\n",ans);
}
return 0;
}
小結:
顯然兩種演算法的複雜度均為O(nlogn),若輸入有序的話時間複雜度就均為O(n),但是紫薯上說“和原始的Graham演算法相比,Andrew演算法更快,且數值穩定性更好”,或許是因為排序二者排序過程中的差異--Graham演算法中的極角排序需要進行大量的叉乘計算。
相關推薦
凸包-Andrew演算法&&Graham掃描法
凸包簡介: 在二維平面上(二維凸包)給出若干個點,能夠包含這若干個點的面積最小的凸多邊形稱為凸包(可以想像有很多個釘子釘在牆上,然後用一個橡皮圈套在所有的釘子上,最後橡皮圈形成的就是一個凸包)。 Graham掃描法: Graham掃描法是一種基於極角排序的進行求解的
計算幾何 : 凸包學習筆記 --- Graham 掃描法
凸包 (只針對二維平面內的凸包) 一、定義 簡單的說,在一個二維平面內有n個點的集合S,現在要你選擇一個點集C,C中的點構成一個凸多邊形G,使得S集合的所有點要麼在G內,要麼在G上,並且保證這個凸多邊
凸包(Convex Hull)構造演算法——Graham掃描法
凸包(Convex Hull) 在圖形學中,凸包是一個非常重要的概念。簡明的說,在平面中給出N個點,找出一個由其中某些點作為頂點組成的凸多邊形,恰好能圍住所有的N個點。 這十分像是在一塊木板上釘了N個釘子,然後用一根繃緊的橡皮筋它們都圈起來,這根橡皮筋的形狀就是所謂的凸包。 計算凸包的一個著名演
計算幾何--凸包--Andrew演算法--HDU1392
題目描述 給出一些點,求凸包的周長。 什麼是凸包 用不嚴謹的話來講,給定二維平面上的點集,凸包就是將最外層的點連線起來構成的凸多邊型,它能包含點集中所有的點。 凸包的Andrew演算法 Andrew演算法是graham的變種。它的思想是這樣的:
凸包演算法(Graham掃描法)
目錄 一、概念 二、演算法步驟 三、程式碼實現 轉自:https://www.cnblogs.com/aiguona/p/7232243.html 一、概念 凸包(Convex Hull)是一個計算幾何(圖形學)中的概念。 在一個實數向量空間V中,對於給定集合X,所有
最小凸包演算法(Convex Hull)(1)-Graham掃描法 -計算幾何-演算法導論
基本問題: 平面上有n個點p1,p2, ..., pn, 要求求出一個面積最小的凸多邊形,使得這個多邊形包含所有平面上的點。 根據演算法導論上提供的兩個方法做一些介紹: 演算法1: Graham掃描法 下面直接給出一段虛擬碼,方便描述: GRAHAM-SCAN(Q) {
凸包演算法詳解-Graham掃描法
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct nod
1015 - 計算幾何之Graham掃描法求凸包 - Cows(POJ 3348)
傳送門 題意 給你一堆點,求這些點的凸包,並求出面積 分析 很久之前就做過的一道題了,還記得那是凱爺(凱爺好厲害好厲害的)講的,是Jarris步進法:按照橫縱座標對所有的點進行排序(橫座標優先) 然後就是和Graham類似的方法了,邊掃描邊
計算幾何之凸包----Graham掃描法
計算幾何之凸包(convexHull)----Graham掃描法 關於凸包的嚴格定義,這裡不打算寫出來,大家可以自行Google或者百度,因為嚴格的數學定義反而不太好理解,用最通俗的話來解釋凸包:給定
尋找凸包(Graham掃描法)
題意描述: 對任意給定的平面上的點集,求最小凸多邊形使得點集中的點要麼在凸多邊形的邊上,要麼在凸多邊形的內部。 Graham演算法描述: 在所有的點中找到一點p0,使得p0的縱座標值最小,在有多個最小縱座標的情況下,找橫座標最小的那一個。 將所有的點
凸包模板(分治 or Graham掃描法)
問題概述:空間上有很多點,現在要用一個凸多邊形將所有點全部包住,求哪些點在這個凸多邊形上 輸入樣例: 對應輸出:
淺談凸包及Graham掃描法
凸包是計算幾何中的一個基本概念。在競賽中,很少單獨考察凸包,但求凸包是很多題目求解的一個關鍵性步驟。 1)凸包的性質 給定一個點集,凸包是能夠包圍所有點的最小凸多邊形。”凸包邊上的點,稱為凸包點,其餘點稱為凸包內點“(引自何援軍著《幾何計算
凸包--Graham掃描法
一直聽大佬們說:凸包、凸包、凸包 一直不會。。。。。 然後。。。。 今天考試,考了一道計算幾何的簡單題。。。。 這,,,還是學一下吧。。 然後考試現場學習一下凸包演算法。 先理解一下凸包是啥東西。 看看這張圖 解釋一下凸包是什麼 如果你有一堆點(原諒我畫的很凌亂) 那麼,找到一個點集 依次連線這些點 使他們
關於凸包——Graham掃描法
首先是凸包的第一種解法,graham掃描法。演算法的步驟如下: 1 首先我們要找到凸包的一個頂點,這個頂點的y座標要最小,相同的y的情況下,選擇更靠左的點 2 對給出的頂點集進行排序,按照極角遞增的順序進行排序?如何進行操作呢,我們可以用我們向量積的性質,如果向量積為正,b
凸包Graham掃描法->HDU3847
Graham掃描法求凸包 凸包定義: 點集Q的凸包(convex hull)是指一個最小凸多邊形,滿足Q中的點或者在多邊形邊上或者在其內。 凸包最常用的凸包演算法是Graham掃描法和Jarvis步進法。 Graham掃描法: 首先
matlab練習程式(尋找凸包,Graham掃描法)
我不太清楚這個凸包在影象處理中到底會怎樣的運用,因為這個好像更多的是計算幾何或是圖形學裡面的東西。不過作為一個演算法,我感覺還是有必要研究一下的。我主要的參考資料是《演算法導論》的33.3和這個部落格。 程式碼在這裡,我只寫了主要過程,過分細節的判斷就省略了。這裡是逆時針尋找: main.m c
二維求凸包 graham掃描法
graham掃描法求凸包的做法就是,選擇一個座標最靠近左下的點,然後作為原點,其他點按極座標排序,角度由小到大,角度想等r小的優先,然後將前兩個點放到棧中進行初始化,之後順序對每一個點進行判斷,只有該點向左發生了偏轉才將該點入棧,否則刪掉當前棧頂元素,再次判斷,直到發生了向
凸包問題—Graham掃描法
#include<iostream> #include<cmath> #include<algorithm> using namespace std; struct point { long long x; long l
凸包問題的Graham-Scan演算法及python實現
基於 Graham-Scan 的凸包求解演算法是在列舉三角形時,採用了更精細的方式,將P_0作為極點,通過極角大小定位最右下側的三角形∆P_0 P_1 P_2,然後讓三角形繞P_0點旋轉,掃描所有輸入點,直到到最左下側為止。 首先要對點集S進行預處理,
計算幾何入門 5:凸包構造演算法下界
從極點法的O(n^4)複雜度,到極邊法的O(n^3),再到增量構造法和Jarvis March的O(n^2),我們經歷了將特定問題演算法不斷優化、降低複雜度的過程。那麼還有比O(n^2)更高效的演算法嗎?凸包構造演算法的下界是什麼?推廣到一般情況,在計算模型固定的情況下特定