题目链接 :线段相交Ⅲ
这题就是数论题目,考察了数学,老实说我不是很会解释这个。
首先要能证明两个线段相交,这个要用到快速排斥实验。很简单知道线段的左端点大于另一线段右端点,或者线段下端点大于另一线段上端点,这俩线段必不相交。
之后用到跨立实验,通过叉积来判断是否线段之间相互位置吗,叉积=0,代表线段端点在另一线段上,<0说明线段两端点在另一线段两侧,>0说明在同一侧。
最后是要求交点。这个要是人来判断就简单多了,画图什么就行,可惜是机器,那就需要用到数学,就是定比分点。不是很会解释,定义如下:
和两点间的中点公式一样,定比分点公式是一种给出中点坐标的公式。定比分点应该理解为:“固定比例分割点的坐标公式”,中点公式是他的一种特殊情况。我们可以用它寻找三角形的内心、质心和外心。它是在一个线段中按照固定比例将线段分为两部分。就是一个点将线段截成两段。该公式可以告知任何一个固定比例的点在坐标系的位置。
然后下面是代码,只能说我数学不太行。
#include<bits/stdc++.h>
#define ll long long
#define N 500005
#define M 105
#define debug cout<<"~~debug "
#define rc p<<1|1
#define lc p<<1
#define PII pair<long long,long long>
#define pb push_back
#define Inf 0x3f3f3f3f
//neuro-sama
const ll mod = 1e9+7;
using namespace std;
inline int read()
{
char c=getchar();int f=1,x=0;
while(c>'9'||c<'0'){ if(c=='-') f=-f;c=getchar();}
while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return f*x;
}
struct node{
double x;
double y;
};
typedef struct node node;
int n,T,m,k;
double cross(node a,node b,node c){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int judge(node a,node b,node c,node d){//快速排斥实验
if(max(a.x,b.x)<min(c.x,d.x)) return 1;
else if(max(a.y,b.y)<min(c.y,d.y)) return 1;
else if(max(c.x,d.x)<min(a.x,b.x)) return 1;
else if(max(c.y,d.y)<min(a.y,b.y)) return 1;
return 0;
}
void solve()
{
node a,b,c,d;
cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y;
if(judge(a,b,c,d)){
cout<<"NO\n";
return;
}
double x=cross(a,b,c);
double xx=cross(a,b,d);
double y=cross(c,d,a);
double yy=cross(c,d,b);
if(x*xx>0||y*yy>0){
cout<<"NO"<<endl;
return;
}
if(x*xx==0||y*yy==0){
cout<<"YES"<<endl;
return;
}
double s=fabs(x)/fabs(xx);
printf("YES (%.3lf %.3lf)\n",(c.x+s*d.x)/(1+s),(c.y+s*d.y)/(1+s));
}
int main()
{
// ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
T=1;
cin>>T;
while(T--)
{
solve();
}
}