求解計算幾何中的凸包問題時,使用的比較簡單,但是複雜度比較高的演算法中有一種演算法叫做極點法,基本思想是從所有的點中去除所有的不是凸包邊上的點.
從所有的剩餘的點中,選出三個點,然後再在剩餘的點集中,判斷是否在三角形的內部,如果在內部,則去除這個點,說明它不是凸包的結果;否則,就暫時保留這個點,說明它可能是凸包邊上的點.如下圖所示:點a在三角形的內部,肯定不是凸包上的點,點b暫時可能是凸包上的點.
不難從上面的圖中看出,若把三角形的三個邊都設定為逆時針方向,那麼點a在三個向量邊的左側.b在其中兩條邊的左側,但是在p和q那條邊的右側.
由此可以判斷,當乙個點在三角形三條逆時針邊的左側時,點必定在三角形的內部,不是凸包邊上的解,否則不是在三角形內部.這就是所謂的toleft測試.
實現toleft測試方法也有很多,最經典的是使用求解矩陣行列式求解有向面積的方式.
設兩個點p和q,測試點s是在pq連線的左側還是右側,如下圖所示.
讓p, q,s構成乙個三角形,然後各個邊,邊的方向與pq的方向一致,這個三角形的有向面積計算如下: 2∗
s=∣∣
∣∣∣p
[x]q
[x]s
[x]p
[y]q
[y]s
[y]p
[z]q
[z]s
[z]∣
∣∣∣∣
2 ∗s
=|p[
x]p[
y]p[
z]q[
x]q[
y]q[
z]s[
x]s[
y]s[
z]|這樣的乙個行列式的計算過程只有乘法和加減法,避免了使用除法或者其他的三角函式計算.寫成**形式就更簡單了.
int area2(point p, point q, point k)
上面的**只是計算有向面積,toleft測試是判斷是否在直線的左側,當這個有向面積大於0的時候,在左側,當小於0的時候,在右側,當等於0 的時候,三點一線,面積為0.
bool toleft(point p, point q, point s)
回頭想一下,這裡其實符合右手定理.
參考鄧老師的課件,根據toleft測試,實現乙個計算凸包的演算法,雖然計算複雜度是o(n^4),但是也是一種解法,下面是實現的乙個**.
#include
#include
using
namespace
std;
struct point
int _x;
int _y;
};int area2(point p, point q, point s)
bool toleft(point p, point q, point s)
int main()
, ,,,
,};vector
flag(points.size(), false);
for (int i = 0; i < points.size(); ++i)}}
}for (int i = 0; i < points.size(); ++i)
return
0;}
極邊法要比極點法快一點,時間複雜度是o(n^3).
#include
#include
#include
using
namespace
std;
struct point
int _x;
int _y;
};int area2(point p, point q, point s)
bool toleft(point p, point q, point s)
int main()
, ,,,
,};vector
flag (points.size(), false);
for (int i = 0; i < points.size(); ++i)
if (leftnotempty == false || rightnotempty == false)}}
for(int i = 0; i< flag.size();++i)
return
0;}
計算幾何 凸包問題
給定平面上的二維點集,求解其凸包。一 graham掃瞄法 1.在所有點中選取y座標最小的一點h,當作基點。如果存在多個點的y座標都為最小值,則選取x座標最小的一點。座標相同的點應排除。然後按照其它各點p和基點構成的向量與x軸的夾角進行排序,夾角由大至小進行順時針掃瞄,反之則進行逆時針掃瞄。實現中無需...
計算幾何 凸包
有多個手機訊號發射器,求解乙個最小區域,要求所有的發射器都包含在這個最小區域中,並且任意兩台發射器之間的交流包含於在這個區域內。將所有的發射器看做為點,任意兩個點之間的連線都包含於乙個平面s 乙個平面的子集s 是凸的,當且僅當 s中的任意兩個點之間的連線都包含於 s中。點集 p的凸包是所有包含 p的...
計算幾何 凸包
如求凸包周長 include include include include include include using namespace std define pi 3.1415926 define eps 1e 10 class point 建立point類,裡面包含很多point的運算子 定...