UVA191 Intersection 题解

本文解析了如何使用向量解决UVA191题目中的线段相交问题,包括快速排斥实验和跨立实验的步骤,并给出了C++代码实现。

UVA191 Intersection 题解

水紫,建议降蓝。

解法

求线段相交裸题,考虑使用向量解决。

我们把矩形的 444 个点两两相连,得到 666 条线段,如果先输入的线段和这 666 条线段都没有交点,则认为线段和矩形不相交,否则认为相交。

向量求两线段是否相交分为两步,快速排斥实验和跨立实验。为了表述方便,我们记两线段分别为 AB,CDAB,CDAB,CD

快速排斥实验

分别以 ABABABCDCDCD 为对角线做水平竖直的两个矩形。如果两个矩形没有相交部分,那两线段不可能相交,我们称为未通过快速排斥实验。否则我们称为通过快速排斥实验。如图。

图

跨立实验

当两线段通过快速排斥实验后,则进行跨立实验。跨立实验即为:验证点 C,DC,DC,D 是否在直线 ABABAB 的两侧同时点 A,BA,BA,B 是否在直线 CDCDCD 的两侧。

根据向量叉乘的几何意义,AB→×AC→\overrightarrow{AB} \times \overrightarrow{AC}AB×AC 为正代表点 CCC 在直线 ABABAB 的逆时针方向,为 000 代表点 CCC 在直线 ABABAB 上,为负代表点 CCC 在直线 ABABAB 的顺时针方向。

如果 AB→×AC→\overrightarrow{AB} \times \overrightarrow{AC}AB×ACAB→×AD→\overrightarrow{AB} \times \overrightarrow{AD}AB×AD 正负性不同,CD→×CA→\overrightarrow{CD} \times \overrightarrow{CA}CD×CACD→×CB→\overrightarrow{CD} \times \overrightarrow{CB}CD×CB 正负性不同,我们称为通过跨立实验。

如果两线段通过了快速排斥实验和跨立实验,则我们认为两线段相交。

为什么要有快速排斥实验

好多人读到了这里,感觉跨立实验足以应付线段相交,为什么还要有快速排斥实验呢?

其实这并不是在减少计算过程,而是保证正确性。如图。

图

两线段会通过跨立实验,如果不进行快速排斥实验,那我们就会错误的认为两线段相交。

代码

#include<bits/stdc++.h>
namespace fast_IO
{
	/**
	 * 快读快写
	*/
};
using namespace fast_IO;
#define int long long
int t,X1,Y1,X2,Y2;
struct point
{
	int x,y;
	point()
	{
		x=y=0;
	}
	point(int x,int y)
	{
		this->x=x,this->y=y;
	}
	inline point operator-(const point &rhs) const
	{
		return point(x-rhs.x,y-rhs.y);
	}
	inline int operator*(const point &rhs)
	{
		return x*rhs.y-y*rhs.x;
	}
};
point po1,po2,po3,po4;
inline int sgn(int x)
{
	return x==0?0:(x>0?1:-1);
}
struct seg
{
	point s,t;
	inline friend bool cross(const seg &lhs,const seg &rhs)
	{
		return std::max(lhs.s.x,lhs.t.x)>=std::min(rhs.s.x,rhs.t.x) && 
		std::max(lhs.s.y,lhs.t.y)>=std::min(rhs.s.y,rhs.t.y) && 
		std::max(rhs.s.x,rhs.t.x)>=std::min(lhs.s.x,lhs.t.x) && 
		std::max(rhs.s.y,rhs.t.y)>=std::min(lhs.s.y,lhs.t.y) && 
		sgn((rhs.s-lhs.s)*(lhs.t-lhs.s))*sgn((rhs.t-lhs.s)*(lhs.t-lhs.s))<=0 && 
		sgn((lhs.s-rhs.s)*(rhs.t-rhs.s))*sgn((lhs.t-rhs.s)*(rhs.t-rhs.s))<=0;
	}
};
seg a,s1,s2,s3,s4,s5,s6;
signed main()
{
	in>>t;
	while(t--)
	{
		in>>a.s.x>>a.s.y>>a.t.x>>a.t.y>>X1>>Y1>>X2>>Y2;
		if(X1>X2) std::swap(X1,X2);
		if(Y1>Y2) std::swap(Y1,Y2);
		po1=(point){X1,Y1},po2=(point){X1,Y2},po3=(point){X2,Y1},po4=(point){X2,Y2};
		s1=(seg){po1,po2},s2=(seg){po2,po4},s3=(seg){po4,po3},s4=(seg){po3,po1};
		s5=(seg){po1,po4},s6=(seg){po2,po3};
		if(!cross(a,s1) && !cross(a,s2) && !cross(a,s3) && !cross(a,s4) && !cross(a,s5) && !cross(a,s6))
			out<<"F\n";
		else out<<"T\n";
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值