题目描述:
线段相交有两种情形:一种是 “规范相交” ,另一种是 “非规范相交”。规范相交是指两条线段恰有唯一 一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:
规范相交认为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
提示:
无
题意解析:
题意很好理解,两线段有一个交点 且 交点不是任意一条线段的端点时为 规范相交 。
具体实现:
首先最容易想到的方法是直接求交点,并判断交点位置,进一步判断是否为规范相交,由于线段上已知两点,故可以求出直线的斜截式方程并求出交点,步骤如下:
在实际运行中,发现会遇到许多特殊情况会导致BUG,例如两条线段在同一条直线上,有一条线段与Y轴平行等,这些情况都需要特殊处理,会使代码冗杂且容易出错,故需要一种快速筛选特殊情况的算法。
第一种便是快速排斥,可以帮我们快速判断两条线段是否可能相交。原理是判断通过两条线段分别在X轴和Y轴上的投影是否有重叠部分,如果没有重叠,故两条线段不可能相交。
第二种是跨立实验,利用叉积,来判断其中一条线段的两端点是否在另一条线段的两侧。
AC代码:
#include<bits/stdc++.h>
using namespace std;
double ax,ay,bx,by,cx,cy,dx,dy,x,y;
double k1,k2,b1,b2;
double minx1,minx2,maxx1,maxx2,miny1,miny2,maxy1,maxy2;
bool contain()
{
if(cx>=min(ax,bx) && cx<=max(ax,bx) && cy>=min(ay,by) && cy<=max(ay,by))
return true;
else
return false;
}
bool cross()
{//求叉积
double j1,j2;
j1=((cx - ax) * (dy - cy) - (cy - ay) * (dx - cx)) / ((bx - ax) * (dy - cy) - (dx - cx) * (by - ay));
j2=((ax - cx) * (by - ay) + (cy - ay) * (bx - ax)) / ((dx - cx) * (by - ay) - (bx - ax) * (dy - cy));
if(j1>=0 && j1<=1 && j2>=0 && j2<=1)
{//利用叉积求交点
x=ax+j1*(bx-ax);
y=ay+j1*(by-ay);
return true;
}
else return false;
}
int main()
{
int t;
cin >> t;
while (t--)
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy);
//利用直线的斜截式求交点
k1=(bx-ax)/(by-ay);
b1=ay-(ax*(ay-by))/(ax-bx);
k2=(dx-cx)/(dy-cy);
b2=cy-(cx*(cy-dy))/(cx-dx);
//求线段边界
maxx1=max(ax,bx);
minx1=min(ax,bx);
maxx2=max(cx,dx);
minx2=min(cx,dx);
maxy1=max(ay,by);
miny1=min(ay,by);
maxy2=max(cy,dy);
miny2=min(cy,dy);
if (maxx1<minx2 || minx1>maxx2 || maxy1<miny2 || miny1>maxy2)
{//快速排斥
printf("NO\n");
continue;
}
// x=(b2-b1)/(k1-k2);
// y=x*k1;
if ((by-ay)*(dx-cx)==(bx-ax)*(dy-cy))
{
if((cy-ay)*(bx-ax)==(cx-ax)*(by-ay))
{
if(contain() )
{
printf("YES\n");
continue;
}
}
printf("NO\n");
continue;
}
if (cross())
{
printf("YES");
if((x != ax || y != ay) && (x != bx || y != by) && (x != cx || y != cy) && (x != dx || y != dy))
{
printf(" (%.3f,%.3f)",x,y);
}
printf("\n");
}
else printf("NO\n");
}
return 0;
}
总结:
快速排斥,叉积