Description
在一张纸上,画着很多个点,小明和小强用这张纸玩游戏,游戏规则如下
1.每人轮流选两个点,然后两点间画一条线段
2.选过的点,不能再选
3.谁首先产生交点,谁就输
4.有人输了,或者剩下的点少于2 个,游戏结束
现在,你作为这个游戏的裁判,需要编写一个程序,判断平面上的两条线段是否相交。
输入格式
第一行输入正整数t,t>64,表示case 个数,后面的t行,每行是8 个整数,x1,y1,x2,y2,x3,y3,x4,y4
坐标范围是[0,32767]
point(x1,y1) point(x2,y2) point(x3,y3) point(x4,y4)互不重合
线段a(point(x1,y1),point(x2,y2))
线段b(point(x3,y3),point(x4,y4))
输出格式
若线段a,b 存在交点输出y,否则输出n。
输入样例
4
0 0 0 1 0 2 0 3
0 0 0 2 0 1 0 3
0 0 2 2 0 2 2 0
0 0 1 1 0 2 2 0
输出样例
n
y
y
y
解题思路
求出一条线段所在直线的直线方程,判断另一线段的端点与直线的相对位置(同侧,异侧还是直线上),
然后对另一条线段同样处理
求直线方程:
已知两点时,直线AX+BY+C=0的一般式方程就是:
A = Y2 - Y1
B = X1 - X2
C = X2*Y1 - X1*Y2
判断点与直线的相对位置:
设点P(x, y),将P代入直线方程得
z = Ax + By + C
z = 0(在直线上)z > 0 or z < 0(在其中一侧)
特殊情况的处理例如:
- 线段穿越直线但不穿越线段
- 线段同线但不相交
注意:
判断相对位置是否异号不要用z1 * z2 < 0来判断,因为计算过程可能溢出
#include<stdio.h>
#include<math.h>
double dis(int x, int y, int a, int b);
int interset(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
int main()
{
int t, x1, y1, x2, y2, x3, y3, x4, y4, flag;
scanf("%d", &t);
while(t --)
{
flag = 0;
scanf("%d %d %d %d %d %d %d %d", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);
flag += interset(x1, y1, x2, y2, x3, y3, x4, y4);
flag += interset(x3, y3, x4, y4, x1, y1, x2, y2);
// 线段都穿过对应直线或其中一线段的至少一个端点在另一线段上
if(flag >= 2)
printf("y\n");
else
printf("n\n");
}
return 0;
}
// 直线(x1, y1) (x2, y2) 线段 (x3, y3), (x4, y4)
int interset(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
{
int a, b, c, t1, t2, flag;
a = y2 - y1;
b = x1 - x2;
c = x2 * y1 - x1 * y2;
// 相对位置
t1 = a * x3 + b * y3 + c;
t2 = a * x4 + b * y4 + c;
// 同号
if((t1 < 0 && t2 < 0) || (t1 > 0 && t2 > 0))
flag = 0;
// 至少一个端点在直线上
else if(t1 == 0 || t2 == 0)
{
其中一个端点在线段上
if(fabs(dis(x3, y3, x1, y1) + dis(x3, y3, x2, y2) - dis(x1, y1, x2, y2)) < 0.00001)
flag = 2;
或者另一端点在线段上
else if(fabs(dis(x4, y4, x1, y1) + dis(x4, y4, x2, y2) - dis(x1, y1, x2, y2)) < 0.00001)
flag = 2;
else
flag = 0;
}
// 异号(线段穿过直线但不一定穿过线段)
else
flag = 1;
return flag;
}
double dis(int x, int y, int a, int b)
{
return sqrt((x - a) * (x - a) + (y - b) * (y - b));
}