1. 程式人生 > >計算幾何 二維凸包問題 Andrew演算法

計算幾何 二維凸包問題 Andrew演算法

凸包:把給定點包圍在內部的、面積最小的凸多邊形。

Andrew演算法是Graham演算法的變種,速度更快穩定性也更好。

首先把所有點排序,按照第一關鍵字x第二關鍵字y從小到大排序,刪除重複點後得到點序列P1...Pn。

1)把P1,P2放入凸包中,凸包中的點使用棧儲存

2)從p3開始,當下一個點在凸包當前前進方向(即直線p1p2)左邊的時候繼續;

3)否則依次刪除最近加入凸包的點,直到新點在左邊。



如圖,新點P18在當前前進方向P10P15的右邊(使用叉積判斷),因此需要從凸包上刪除點P15和P10,讓P8的下一個點為P18。重複該過程直到碰到最右邊的Pn,求出下凸包即凸包的下輪廓。然後從Pn反過來重複該步驟求出上凸包,合併起來後就是完整的凸包。


該演算法掃描為O(n)複雜度,排序O(nlogn)。

C++ code(劉汝佳《演算法競賽入門經典-訓練指南》模板)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const double eps = 1e-8,Pi=3.14159265;

int n;

inline int dcmp(double x)//三態函式
{
	if(fabs(x) < eps)	return 0;
	else return x>0 ? 1 : -1;
}

#define Vector Point
struct Point
{
	double x,y;
	inline Point(double x=0,double y=0):x(x),y(y) {}
}p[10000+5],ch[10000+5];

bool myCmp(Point A, Point B)
{
	if(A.x != B.x)	return A.x < B.x;
	else return A.y < B.y;
} 

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);}
bool operator == (const Vector& A, const Vector& B) {return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;}

inline double Cross(Vector A, Vector B)//叉積
{
	return A.x * B.y - A.y * B.x;
}

int ConvexHull()
{
	sort(p,p+n,myCmp);
	int m = 0;
	for(int i = 0; i <n; i++)
	{
		while(m > 1 && dcmp(Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])) <= 0) m--;
		ch[m++] = p[i];
	}
	int k = m;
	for(int i = n-2; i >= 0; i--)
	{
		while(m > k && dcmp(Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])) <= 0) m--;
		ch[m++] = p[i];
	}
	if(n > 1) m--;
	return m;
}

double Dis(Point A, Point B)
{
	return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}

int main()
{
	int m;
	cin>>n;
	for(int i = 0; i < n; i++)
	{
		int a,b;
		cin>>a>>b;
		p[i] = Point(a,b);
	}
	m = ConvexHull();
	//計算凸包周長 
	double ans=0.0;
	for(int i = 0; i < m-1; i++)
		ans += Dis(ch[i],ch[i+1]);
	ans += Dis(ch[m-1],ch[0]);
	printf("%.1f",ans);
} 

例題:

wikioi1298(click me)

求解二維凸包並計算周長

wikioi3201(click me)

求解二維凸包並計算周長

poj1113(click me)

本題翻譯:http://blog.csdn.net/lytning/article/details/24046075

求解二維凸包並加入一個圓的周長