描述
线段相交有两种情形:一种是“规范相交”,另一种是“非规范相交”。规范相交是指两条线段恰有唯一一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:
规范相交认为a,b两种情况都是不相交的,而非规范相交认为a,b两种情况都是相交的。
本题要求判断两条线段是否相交。如果是规范相交则输出YES,并输出交点坐标,如果是非规范相交则只需输出YES,如果不相交则输出NO。
输入
输入有多组数据,T表示输入数据的组数。每组测试数据有两行第一行输入一条线段的两个端点的坐标,第二行输入另一个线段的两个端点的坐标。
输出
对于每组测试数据,输出一行。如果是规范相交则输出YES,并输出交点坐标(小数点后面保留3位),如果是非规范相交则只需输出YES,如果不相交则输出NO。
题解
先要知道两线段叉乘的结果是面积
使用叉乘,令四个端点为a,b,c,d;
double cross(node a,node b,node c)
{
return (b.x -a.x )*(c.y -a.y )-
(c.x -a.x )*(b.y -a.y );
}
使用上面的式子可以判断c,在直线ab的哪一边;
这样我们只要使cross(a,b,c)*cross(a,b,d)<0就可以保证c,d在直线ab的两端;
同理我们使cross(c,d,a)*cross(c,d,b)<0 保证a,b在直线cd的两端;
我们令
k1=cross(a,b,c)*cross(a,b,d)
k2=cross(c,d,a)*cross(c,d,b);
只要k1,k2都小于0一定相交
k1,k2有一个大于0一定不相交
当k1,k2都等于0表示在同一直线,用两线段的半径和两线段中心点之间的距离判断是否相交
当k1,k2有一个等于0,是//T字型相交
重点是求解交点坐标,使用两线段叉乘的结果是面积
注意d1,d2要加绝对值
d1=ab*h1;
d2=ab*h2;
h=h1/(h1+h2)=d1/(d1+d2);
我们就知道ce在cd中的比例
#include<bits/stdc++.h>
using namespace std;
int t;
struct node
{
double x,y;
}p[8];
double cross(node a,node b,node c)
{
return (b.x -a.x )*(c.y -a.y )-
(c.x -a.x )*(b.y -a.y );
}
double dis(node s,double x,double y)
{
return sqrt((s.x -x)*(s.x -x)+
(s.y -y)*(s.y -y));
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=4;i++)
{
scanf("%lf%lf",&p[i].x ,&p[i].y );
}
double k1=cross(p[1],p[2],p[3])*
cross(p[1],p[2],p[4]);
double k2=cross(p[3],p[4],p[1])*
cross(p[3],p[4],p[2]);
if(k1>0||k2>0)
{
printf("NO\n");
}
else if(k1<0&&k2<0)
{
double d1=cross(p[1],p[2],p[3]);
double d2=cross(p[1],p[2],p[4]);
d1=fabs(d1);
d2=fabs(d2);
double h=d1/(d1+d2);
double x=(p[4].x -p[3].x )*h+p[3].x ;
double y=(p[4].y -p[3].y )*h+p[3].y ;
printf("YES (%0.3f,%0.3f)\n",x ,y );
}
else if(k1==0&&k2==0)//在同一直线上用两线段的半径和两线段中心点之间的距离判断是否相交
{
double x1=(p[1].x +p[2].x )/2;
double y1=(p[1].y +p[2].y )/2;
double x2=(p[3].x +p[4].x )/2;
double y2=(p[3].y +p[4].y )/2;
double d=dis(p[1],x1,y1)
+dis(p[3],x2,y2);
if(d<=sqrt((x1-x2)*(x1-x2)+
(y1-y2)*(y1-y2))) printf("NO\n");
else printf("YES\n");
}
else printf("YES\n");//T字型相交
}
return 0;
}