TZOJ3348: 线段相交Ⅲ(快速排斥,跨立,求两线段交点)

描述

线段相交有两种情形:一种是“规范相交”,另一种是“非规范相交”。规范相交是指两条线段恰有唯一一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:

规范相交认为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;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值