给你两个向量 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×y2−x2×y1
那么有啥子实际意义呢?
设以线段 P 1 P 2 P_1P_2 P1P2 为对角线的矩形为R, 设以线段 Q 1 Q 2 Q_1Q_2 Q1Q2 为对角线的矩形为T,如果R和T不相交,显然两线段不会相交。
轻松写出快速跨立实验代码
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;
}
在上图中,线段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点相交,如下图
上图中,线段AB与CD相交于C点,按照之前介绍的方法,我们可以连成两向量AD和AC,这时候,我们发现,AC与AB共线,AB×AC = 0;而AB×AD < 0;两者并不异号,可实际上仍然相交。所以当出现两叉乘结果中,有一方为0,也可以看成点CD在直线AB的两边。
2.两条线段重合,如下图:
对于这两种情况只要微微修改向量叉积代码就可以实现
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;
}