计算几何2- 判断两线段是否相交

前置知识:向量叉积

给你两个向量 a ⃗ = ( x 1 , y 1 ) \vec{a}=(x_1,y_1) a =(x1,y1) b ⃗ = ( x 2 , y 2 ) \vec{b}=(x_2,y_2) b =(x2,y2)
我们定义他们的叉积
a ⃗ × b ⃗ = x 1 × y 2 − x 2 × y 1 \vec{a} \times \vec{b}=x_1 \times y_2 - x_2 \times y_1 a ×b =x1×y2x2×y1
那么有啥子实际意义呢?

  1. 叉积的正负:
    a ⃗ × b ⃗ < 0 \vec{a}\times\vec{b} < 0 a ×b <0,表示 b ⃗ \vec{b} b 在向量 a ⃗ \vec{a} a 的顺时针方向
    a ⃗ × b ⃗ > 0 \vec{a}\times\vec{b} > 0 a ×b >0,表示 b ⃗ \vec{b} b 在向量 a ⃗ \vec{a} a 的逆时针方向
    a ⃗ × b ⃗ = 0 \vec{a}\times\vec{b} = 0 a ×b =0, 表示向量a与向量b平行。
  2. 叉积的绝对值
    ∣ a ⃗ × b ⃗ ∣ |\vec{a}\times\vec{b} | a ×b 的值是以 a ⃗ \vec{a} a , b ⃗ \vec{b} b 为临边的平行四边形面积
    当然,你想要三角形面积除2就可以了

判断相交两个阶段

快速排斥(跨立)试验

设以线段 P 1 P 2 P_1P_2 P1P2 为对角线的矩形为R, 设以线段 Q 1 Q 2 Q_1Q_2 Q1Q2 为对角线的矩形为T,如果R和T不相交,显然两线段不会相交。
计算几何2- 判断两线段是否相交_第1张图片
轻松写出快速跨立实验代码

struct point{
    double x,y;
};

struct vec{
    point st,ed;
}a,b;

double cj(point a,point b,point c){
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

bool jduge(vec p,vec q){
    if(min(p.st.x,p.ed.x)>max(q.st.x,q.ed.x))return false;
    if(min(p.st.y,p.ed.y)>max(q.st.y,q.ed.y))return false;
    if(min(q.st.x,q.ed.x)>max(p.st.x,p.ed.x))return false;
    if(min(q.st.y,q.ed.y)>max(p.st.y,p.ed.y))return false;
    return true;
}

叉积判断

计算几何2- 判断两线段是否相交_第2张图片
在上图中,线段AB与线段CD相交,于是我们可以得到两个向量AC,AD,C和D分别在AB的两边,向量AC在向量AB的逆势针方向,AB×AC > 0;向量AD在向量AB的顺势针方向,AB×AD < 0,两叉乘结果异号。
这样,方法就出来了:如果线段CD的两个端点C和D,与另一条线段的一个端点(A或B,只能是其中一个)连成的向量,与向量AB做叉乘,若结果异号,表示C和D分别在直线AB的两边,若结果同号,则表示CD两点都在AB的一边,则肯定不相交。
当然,不能只证明C,D在直线AB的两边,还要用相同的方法证明A,B在直线CD的两边,两者同时满足才是线段相交的充要条件。
不过,线段相交还有一些特殊情况:

1.只有1点相交,如下图
计算几何2- 判断两线段是否相交_第3张图片
上图中,线段AB与CD相交于C点,按照之前介绍的方法,我们可以连成两向量AD和AC,这时候,我们发现,AC与AB共线,AB×AC = 0;而AB×AD < 0;两者并不异号,可实际上仍然相交。所以当出现两叉乘结果中,有一方为0,也可以看成点CD在直线AB的两边。

2.两条线段重合,如下图:
计算几何2- 判断两线段是否相交_第4张图片
对于这两种情况只要微微修改向量叉积代码就可以实现

if(cj(p.st,q.st,p.ed)*cj(p.st,p.ed,q.ed)<0)return false;
if(cj(q.st,p.st,q.ed)*cj(q.st,q.ed,p.ed)<0)return false;

最后我们得到总代码
可以在51Nod1264题提交试试

#include 
#include 
#include 
#include 
using namespace std;

struct point{
    double x,y;
};

struct vec{
    point st,ed;
}a,b;

double cj(point a,point b,point c){
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

bool jduge(vec p,vec q){
    if(min(p.st.x,p.ed.x)>max(q.st.x,q.ed.x))return false;
    if(min(p.st.y,p.ed.y)>max(q.st.y,q.ed.y))return false;
    if(min(q.st.x,q.ed.x)>max(p.st.x,p.ed.x))return false;
    if(min(q.st.y,q.ed.y)>max(p.st.y,p.ed.y))return false;

    if(cj(p.st,q.st,p.ed)*cj(p.st,p.ed,q.ed)<0)return false;
    if(cj(q.st,p.st,q.ed)*cj(q.st,q.ed,p.ed)<0)return false;

    return true;
}

int main(){
    int r;
    cin>>r;
    while(r--){
        cin>>a.st.x>>a.st.y>>a.ed.x>>a.ed.y>>b.st.x>>b.st.y>>b.ed.x>>b.ed.y;
        if(jduge(a,b))cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

你可能感兴趣的:(#,计算几何,算法学习总结)