线段相交
Total Submit:328 Accepted:71
Description
你将判断给定线段L1,L2是否相交, 其中L1表示为s1x,s1y,e1x,e1y,L2表示为s2x,s2y,e2x,e2y
Input
多组数据输入,每组一行,每组八个浮点数,s1x,s1y,e1x,e1y,s2x,s2y,e2x,e2y
Output
相交则输出yes否则输出no
Sample Input
0 1 2 1 1 0 1 2
1 1 2 2 3 3 4 4
Sample Output
yes
no
Source
NUAA
分析:线段相交的判断用向量积做比较方便。但是逻辑一定要清晰。大一学的线性代数记忆已经不深刻了。遇到了一些小问题。当然还可以有其他做法,比如构造方程判断是否有解等等。
向量积回顾:
|向量a×向量b|=|a||b|sinθ在这里θ表示两向量之间的角夹角(0° ≤ θ ≤ 180°),它位于这两个矢量所定义的平面上。
关键点在于求得的向量积的方向问题。这就取决于向量a与向量b的夹角。(这里的向量写的时候在字母上面加上向右的箭头)。
一般的直接比较两条线段的x轴最大与最小值 以及 y轴的最大与最小值可以直接判断是否相交。但是出现以下情况(显然这2条线段是不相交的):
这就需要向量积(叉积)发挥作用了!
向量ab(b.x-a.x,b.y-a.y)和向量ac的叉积=ab的长度*ac的长度*sin(从ab到ac转过的角度)=(b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x)。
所以方向是垂直于纸面向外(想起物理了 :))。
这里要非常小心叉积的顺序。顺序不一样,它们的夹角也不一样。夹角总是从前面的向量逆时针转到后面的向量的!!!
所以只要保证 ab x ac 得到的向量积的方向与ad x ab一致,表达式即为它们的乘积 >= 0。
同理:cd x ca (向量积)的方向与 cb x cd(向量积) 的方向也应该保持一致。
上图中 cd 与 ca 的向量积小于0,cb 与 cd 的向量积大于0,不符合要求,所以不相交。
#include<stdio.h>
//线段相交——向量积
struct point
{
double x;
double y;
};
double min(double x, double y)
{
return x<y ? x : y;
}
double max(double x, double y)
{
return x>y ? x : y;
}
double mul(point a, point b, point c) // AB->(向量) x AC->(向量) 叉积
{
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
bool judge(point a, point b, point c, point d)
{
return ( (max(a.x, b.x) >= min(c.x, d.x)) &&
(max(c.x, d.x) >= min(a.x, b.x)) &&
(max(a.y, b.y) >= min(c.y, d.y)) &&
(max(c.y, d.y) >= min(a.y, b.y)) &&
(mul(a, b, c) * mul(a, d, b) >= 0) &&
(mul(c, d, a) * mul(c, b, d) >= 0) );
}
int main()
{
point a, b, c, d;
while(scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&a.x,&a.y,&b.x,&b.y,&c.x,&c.y,&d.x,&d.y) != EOF)
{
if(judge(a, b, c, d) == true)
printf("yes\n");
else
printf("no\n");
}
return 0;
}