凸包 graham旋轉掃描

凸包(Convex Hull)是一個計算幾何(圖形學)中的概念。
在一個實數向量空間V中,對於給定集合X,所有包含X的凸集的交集S被稱爲X的凸包
X的凸包可以用X內所有點(X1,...Xn)的線性組合來構造.
在二維歐幾里得空間中,凸包可想象爲一條剛好包著所有點的橡皮圈。

用不嚴謹的話來講,給定二維平面上的點集,凸包就是將最外層的點連接起來構成的凸多邊型,它能包含點集中所有的點。


給我們n個點,讓我們找出這個凸包外面一圈經過的節點。

graham旋轉掃描就是來解決這個問題的。

過程
⒈ 在所有點中選取y座標最小的一點H,當作基點。如果存在多個點的y座標都爲最小值,則選取x座標最小的一點。座標相同的點應排除。然後按照其它各點p和基點構成的向量<H,p>;與x軸的夾角進行排序,夾角由大至小進行順時針掃描,反之則進行逆時針掃描。實現中無需求得夾角,只需根據餘弦定理求出向量夾角的餘弦值即可(當然,用函數直接算出角度就可以了。這裏插一句,一開始以爲atan就可以了,結果它返回的範圍時負二分之π~二分之π,明顯不是我們要的結果,我們要的是和x正半軸的夾角,也就是0~π,所以要用atan2(y,x),表示座標原點爲起點,指向(y,x)的射線在座標平面上與x軸正方向之間的角的角度度(-π~π))。以下圖爲例,基點爲H,根據夾角由小至大排序後依次爲H,K,C,D,L,F,G,E,I,B,A,J。下面進行逆時針掃描。
⒉ 線段<H,K>;一定在凸包上,接着加入C。假設線段<K,C>;也在凸包上,因爲就H,K,C三點而言,它們的凸包就是由此三點所組成。但是接下來加入D時會發現,線段<K,D>;纔會在凸包上,所以將線段<K,C>;排除,C點不可能是凸包。
⒊ 即當加入一點時,必須考慮到前面的線段是否會出現在凸包上。從基點開始,凸包上每條相臨的線段的旋轉方向應該一致,並與掃描的方向相反。如果發現新加的點使得新線段與上線段的旋轉方向發生變化,則可判定上一點必然不在凸包上。實現時可用向量叉積進行判斷,設新加入的點爲pn + 1,上一點爲pn,再上一點爲pn - 1。順時針掃描時,如果向量<pn - 1,pn>;與<pn,pn + 1>;的叉積爲正(逆時針掃描判斷是否爲負),則將上一點刪除。刪除過程需要回溯,將之前所有叉積符號相反的點都刪除,然後將新點加入凸包。
在上圖中,加入K點時,由於線段<H,C>要旋轉到<H,K>的角度,爲順時針旋轉,所以C點不在凸包上,應該刪除,保留K點。接着加入D點,由於線段<K,D>要旋轉到<H,K>的角度,爲逆時針旋轉,故D點保留。按照上述步驟進行掃描,直到點集中所有的點都遍歷完成,即得到凸包。
這個算法可以直接在原數據上進行運算,因此空間複雜度爲O⑴。但如果將凸包的結果存儲到另一數組中,則可能在代碼級別進行優化。由於在掃描凸包前要進行排序,因此時間複雜度至少爲快速排序的O(nlgn)。後面的掃描過程複雜度爲O(n),因此整個算法的複雜度爲O(nlgn)。
下面給出一題的代碼:

題目大意:且凸包的邊的長度

#include<cstdio>
#include<iostream>
#include <cmath>
#include <algorithm>
using namespace std;
int n;
struct point{
	int x,y;
	double angle;
}s[100010];
bool cmp2(point a,point b){
	return a.angle<b.angle;
}
int chch(point a,point b,point c){
	point x1,x2;
	x1.x=b.x-a.x,x1.y=b.y-a.y;
	x2.x=c.x-b.x,x2.y=c.y-b.y;
	return x1.x*x2.y-x1.y*x2.x;
}
point tubao[100010];
int main()
{
    cin>>n;
	int t=1;
	for(int i=1;i<=n;i++){
		cin>>s[i].x>>s[i].y;
		if(s[i].y<s[t].y)t=i;
		else if(s[i].y==s[t].y&&s[i].x<s[t].x)t=i;
	}
	swap(s[n],s[t]);
	for(int i=1;i<n;i++){
		if(s[i].x-s[n].x==0)s[i].angle=3.1415926/2;
		else s[i].angle=atan2( (s[i].y-s[n].y),(s[i].x-s[n].x) );
	}
	sort(s+1,s+n,cmp2);
	t=1;
	tubao[1]=s[n];
	for(int i=1;i<n;i++){
		while((t>1)&&(chch(tubao[t-1],tubao[t],s[i])<=0))t--;
		t++;
		tubao[t]=s[i];
	}
	double ans=0;
	for(int i=1;i<=t;i++){
        int b=i+1;
        if(i==t)b=1;
		ans+=pow(pow((tubao[i].x-tubao[b].x)*1.0,2)+pow((tubao[i].y-tubao[b].y)*1.0,2),1.0/2);
	}
	printf("%.1f",ans);
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章