1. 程式人生 > >快速排斥、跨立實驗判斷線段是否相交

快速排斥、跨立實驗判斷線段是否相交

寫在前面

在其他部落格中看到這方面的知識,很多都是重複,並且說的總是雲裡霧裡的,所以這裡我就自己總結一下這種問題如何求解,判斷兩個線段是否相交在前面我們提到了會用到叉積的一點知識,那麼這裡就來詳細說一下怎麼判斷兩個線段是否相交

演算法詳解

首先我們看一下快速排斥實驗,快速排斥實驗也就是以兩條線段作為對角線做矩形,判斷兩個矩形是否相交,那麼我們這裡可以知道:

1)如果兩個矩形不相交,那麼線段一定不相交

2)如果兩個矩形相交,那麼線段不一定相交,如下圖


所以這裡我們首先就要判斷兩條線段形成的矩形是否相交,只有相交我們才要繼續進行判斷後面的線段是否相交.......

跨立實驗:前面我們知道叉積可以用來判斷兩個向量之間的位置關係(順時針還是逆時針關係),那麼這裡我們就會用到這個性質

我們知道如果兩個線段相交的話,那麼一條線段兩邊的兩個點要位於另一條線段的兩邊,只有兩條線段都滿足這個條件,我們就可以判定這兩條直線相交了,那麼我們這裡所說的一條線段兩個端點位於另一條線段的兩邊,這就是其他部落格中提到的跨立吧

那麼我們就用叉積來對是否滿足這個條件進行判斷:



取其中一個向量作為中間向量,中間向量中開始端點作為另外兩個向量的起點,判斷三個向量之間的位置關係即可:

第一個圖中: (ca × cd)(cd × cb) >= 0 我們即可判斷滿足跨立條件

第二個圖中: (bc × ba)(ba × bd) >=0 我們即可判斷滿足跨立條件

第三個圖中: (bc × ba)(ba × bd)  < 0不滿足跨立條件

第四個圖中: (ca × cd)(cd × cb) >= 0我們即可判斷滿足跨立條件

那麼我們就可以知道上面條件就是判斷跨立是否成立的條件了,那麼這樣我們線段是否相交就已經可以解決了.

栗子及模板

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;
const int MAXN = 2100;
struct Point
{
    double x,y;
}line[MAXN][2];

double mult(Point p0,Point p1,Point p2)  //叉積計算,p0為公用節點
{
    return (p0.x - p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x);
}
//aa、bb屬於同一個矩形     cc、dd屬於同一個矩形         相交返回true,不相交返回false
bool Judge(Point aa,Point bb,Point cc,Point dd)    
{
    //判斷兩個形成的矩形不相交
    if(max(aa.x , bb.x) < min(cc.x , dd.x)) return false;     
    if(max(aa.y , bb.y) < min(cc.y , dd.y)) return false;
    if(max(cc.x , dd.x) < min(aa.x , bb.x)) return false;
    if(max(cc.y , dd.y) < min(aa.y , bb.y)) return false;
    //現在已經滿足快速排斥實驗,那麼後面就是跨立實驗內容(叉積判斷兩個線段是否相交)
    if(mult(aa,cc,bb) * mult(aa,bb,dd) < 0) return false;           //正確的話也就是aa,bb要在cc或者dd的兩邊
    if(mult(cc,aa,dd) * mult(cc,dd,bb) < 0) return false;
    return true;
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        bool flag = true;
        for(int i = 0;i < n;i ++)
            scanf("%lf%lf%lf%lf",&line[i][0].x,&line[i][0].y,&line[i][1].x,&line[i][1].y);
        for(int i = 0;i < n;i ++)
            for(int j = i+1;j < n;j ++)
            {
                if(Judge(line[i][0],line[i][1],line[j][0],line[j][1])) // 判斷兩條直線是否相交
                {
                    flag = false;
                    break;
                }
                if(!flag) break;
            }
        if(!flag) printf("burned!\n");
        else printf("ok!\n");
    }
    return 0;
}

參考部落格