計算幾何中,判斷線段是否相交是最基本的題目。 所謂幾何, 最基本的當然就是坐標, 從坐標中我們可以知道位置和方向,比如:一個點就是一個位置,兩點確定一條直線,從某點指向另一點的有向線段所在的直線是一向量。要處理幾何題,我們又不得不涉及到叉積和點積, 判斷線段相交就要用到叉積。
下面先講講相交的形式:
說到線段, 我們很自然想到直線,判斷兩條直線是否相交只需判斷它們斜率是否相等,相等就為平行或重合, 不等就相交(注:判斷相交我們不采用除法,因為除法容易產生浮點誤差,當兩條直線斜率接近時,很容易出錯。 事實上,幾乎所有幾何題都不建議采用除法)。
線段相交有兩種形式:
規范相交和 非規范相交 。 區別就是交點是否是其中一條線段的端點,不是的是規范相交。
叉積的概念: 設向量 a(x1, y1) 、 b(x2, y2) ;
a x b = x1*y2 - x2*y1; (與數學中的叉積不太一樣)
判斷線段相交比較繁瑣,主要就是判斷異側:
我們以一條線段的一端點為起點,沿着線段方向看去(一條射線),在左手邊為逆時針方向,右手邊為順時針方向。如果另一線段兩端點分別在這一線段的兩側,那么線段可能相交(也可能在線段外),否則不可能相交。對另一線段采用相同方法就可判斷出是否相交了。
這個過程主要通過叉積來判斷: 叉積大於 0 ,在點在向量的順時針方向,小於 0 , 在逆時針方向 ; 等於 0, 端點在直線上。
具體實現:
設:線段 a :P1(x1, y1)、P2(x2, y2) 線段 b: Q1(x3, y3)、Q2(x4, y4)
d1 ====> (P2 - P1) x (Q1 - P1) (叉積)
d2 ====> (P2 - P1) x (Q2 - P1) (叉積)
d3 ====> (Q2 - Q1) x (P1 - Q1) (叉積)
d4 ====> (Q2 - Q1) x (P2 - P1) (叉積)
首先,先判斷端點是否在另一線段上。
然后,我們只需判斷 d1 * d2 < 0 並且 d3 * d4 < 0 便可判斷線段相交。
1 #define cs const 2 #define cp const P& 3 #define op operator 4 const double eps = 1e-8; 5 inline int sig(double x) {return (x>eps)-(x<-eps);} 6 7 struct P{ 8 double x, y; 9 void in() { scanf("%lf%lf", &x, &y); } 10 P(double x=0.0, double y=0.0) : x(x), y(y) {} 11 12 P op-(cp a)cs { return P(x-a.x, y-a.y); } 13 double op^(cp a)cs { return x*a.y - y*a.x; } //叉積 14 double op*(cp a)cs {return x*a.x + y*a.y;} 15 16 double cross(P a, P b) { return (a-*this) ^ (b-*this); } 17 double dot(P a, P b) { return (a-(*this)) * (b-(*this)); } 18 bool on_seg(P a, P b) { return !sig(cross(a, b)) && sig(dot(a, b)) <= 0; }//判斷是否在點上 19 }; 20 21 bool seg(P a, P b, P c, P d) { //判斷相交(a - b)線段 、(c - d)線段 22 if(a.on_seg(c, d) || b.on_seg(c, d) || c.on_seg(a, b) || d.on_seg(a, b)) 23 return true; 24 return sig(a.cross(b, c)*a.cross(b, d)) < 0 && sig(c.cross(d, a)*c.cross(d, b)) < 0; 25 }
訓練題:杭電oj 1086 :
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1086