弱校的奮鬥之計算幾何之旅(1)——向量的引入

接觸ACM大概有一年了,這一年來,學習了不少算法,但是,像我們這種弱校,教練是掛名的,算法都是師兄們一代又一代流傳下來的,基本上師兄們會什麼算法,就教我們什麼算法了。我記得我高中的數學老師說過:“你們要超越老師,不然一代又一代傳下去,會一直衰落的。”我們師兄流傳下來的算法基本上都是圖論,數據結構。而數論、幾何這些方面存在着一個很大的缺口,每次遇到這類型的題目也只能望而生畏了。於是我決定要向這些方向進軍,嘗試找到新的突破。即使是弱校,也不能阻止我們熱愛ACM。

計算幾何

我看了算法導論和lrj的黑書,雖然黑書上有些地方暫時還沒看懂,但是還算略有小成。

 

1、點、線段的表示:

點的座標表示我麼很容易想到用結構體:

typedef struct _point
{
	int x,y;
}Point;


而線段,我們可以用兩個點表示。

 

2、如何判斷兩條直線、線段是否相交:

用高中解析幾何的知識我們很容易想到判斷兩條直線是否相交,只需要用求出兩條直線的斜率,判斷斜率是否相等就行了。如果是線段,只需要求出兩直線交點,判斷是否在兩條線段兩端點之間就行。然而,實際上由於在求斜率和交點時浮點型的數值運算需要考慮精度問題,而且不能體現算法的美。

在計算幾何中,我們常常將問題轉化爲向量的關係。

假設向量a=(x1,y1),向量b=(x2,y2),只需要判斷x1*y2-x2*y1是否等於0就可以判斷兩直線是否相交了。

但是對於線段,我們要怎樣判斷是否相交呢?

我們先在這裏引入“向量的左右”這一個概念。如下圖1,假設我們咱在向量p0p1的起點p0,面向p0p1向量的正方向,走手邊即左側,右手邊爲右側。

位於向量逆時針方向的向量,我們稱之爲“右手螺旋”,如上圖2,反之成爲“左手螺旋”,如上圖3。

我們在判斷兩線段是否相交時,只需要判斷兩向量是否相互“跨越”對方。所謂跨越,即一個向量的兩個端點分別位於另一個向量的左右手方向。如下圖:

圖4中,兩向量並沒有相互“跨越”,因爲p0和p1同時在向量p2p3的右側。圖5中,兩向量互相“跨越”了對方,所以我們就可以說線段p0p1和線段p2p3相交了。

 

3、點積和叉積:

但是,我們該如何表示點在向量的左右方向呢?

這是我們介紹一個在計算幾何裏很重要的運算:點積和叉積。

回憶高中所學的知識,假設p0爲(0,0),我們知道p1Xp2是(0,0),p1,p2和p1+p2所形成的平行四邊形的有面積,注意這裏的是“有向面積”,如下圖陰影部分。

p1Xp2 = x1y2 - x2y1 = |p1|*|p2|*sinθ

如果p1Xp2爲正數,那麼相對於(0,0),p2位於p1的右手螺旋方向,如果p1Xp2爲負數,那麼p2位於p1的左手螺旋方向。於是我們運用叉積,就能判斷點在向量的左右方向了。

int cross (Point p0,Point p1,Point p2)
{//求p0p1和p0p2的叉積
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

現在我們來考慮特殊情況:

假設向量中的一個點在另一條向量上,如上圖7,此時,p0p1Xp0p2=0,此時該如何判斷呢?很容易能想到只要用座標判斷p2是否在p0和p1之間就行。另外,我們還有另一種方法,用向量的點積能判斷。

向量p1·p2 = |p1|*|p2|*cosθ,如下圖:

只需要判斷向量p2p0和p2p1的點積就能判斷點是否在線段上。當點積小於0,表示p2在線段p0p1上,當點積大於0,表示不在線段上。

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