描述
线段相交有两种情形:一种是“规范相交”,另一种是“非规范相交”。规范相交是指两条线段恰有唯一一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:
规范相交认为a,b两种情况都是不相交的,而非规范相交认为a,b两种情况都是相交的。
本题要求判断两条线段是否相交。如果是规范相交则输出YES,并输出交点坐标,如果是非规范相交则只需输出YES,如果不相交则输出NO。
输入
输入有多组数据,T表示输入数据的组数。每组测试数据有两行第一行输入一条线段的两个端点的坐标,第二行输入另一个线段的两个端点的坐标。
输出
对于每组测试数据,输出一行。如果是规范相交则输出YES,并输出交点坐标(小数点后面保留3位),如果是非规范相交则只需输出YES,如果不相交则输出NO。
样例输入
4
0 0 1 1
0 1 1 0
0 0 2 2
2 2 3 3
0 0 2 2
1.5 1.5 3 3
0 0 1 1
2 2 3 3
样例输出
YES (0.500,0.500)
YES
YES
NO
可以用快速排斥实验和跨立实验先排除“NO”(不能相交的情况),让后用叉积排除只能输出“YES”(有端点在另一条线段上和线段重叠的情况,此时同向的叉积的值为0),最后只有要求交点的情况了。
求交点坐标:
根据三角形面积等于两条边的叉积的二分之一,我们就可以求出交点在CD线段上的比例位置,看下图更直观。
知道了比例我们就可以用向量法求出点的坐标。
#include <bits/stdc++.h>
using namespace std;
struct node {
double x, y;
} A, B, C, D;
int qp() {//快速排斥实验
if (min(C.x, D.x) <= max(A.x, B.x) &&
min(A.x, B.x) <= max(C.x, D.x) &&
min(C.y, D.y) <= max(A.y, B.y) &&
min(A.y, B.y) <= max(C.y, D.y))
return 1;
return 0;
}
double cross(struct node A1, struct node B1, struct node C1, struct node D1) {//直线A1B1与直线C1D1的叉积
double ABx = B1.x - A1.x;
double ABy = B1.y - A1.y;
double CDx = D1.x - C1.x;
double CDy = D1.y - C1.y;
return ABx * CDy - CDx * ABy;
}
int kl() {//跨立实验
if (cross(A, B, A, D) * cross(A, B, A, C) <= 0 &&
cross(C, D, C, A) * cross(C, D, C, B) <= 0)
return 1;
return 0;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
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);
if (!qp() || !kl()) {//不相交
printf("NO\n");
continue;
}
if (cross(A, B, C, D) == 0 ||
cross(A, B, A, C) == 0 ||
cross(A, B, A, D) == 0 ||
cross(C, D, C, A) == 0 ||
cross(C, D, C, B) == 0) {//有端点在另一条线段上或线段重叠必有一个叉积为0
printf("YES\n");
continue;
}
printf("YES ");
double p1 = cross(A, B, A, C), p2 = cross(A, B, A, D);//求交点
double s = abs(p1) / abs(p2);
printf("(%.3f,%.3f)\n", (C.x + s * D.x) / (1 + s), (C.y + s * D.y) / (1 + s));
}
return 0;
}