线段相交Ⅲ
3348: 线段相交Ⅲ
时间限制(普通/Java):1000MS/3000MS 内存限制:64000KByte
描述
线段相交有两种情形:一种是“规范相交”,另一种是“非规范相交”。规范相交是指两条线段恰有唯一一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:
规范相交认为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
思路
计算几何模板题
两直线状态有非相交和相交,相交又可以分成规范相交和非规范相交
判断两直线是否相交,看每条线段两点是否在另一条线的两边
若有线段两段点在另一线段同一边,则不相交,否则两线相交
在相交的情况下,如果有一条线段的段点在另一线段上,则为非规范相交,否则为规范相交。
这里可以用向量来判断两线是否相交
按图建立三个向量,然后 b1a1向量 和 b1a2向量 分别与 b1b2向量叉乘,得到的结果如果为正数,则点在向量右边,为负数则点在向量左边,若为零,则点在向量所在直线上。
这里将四个端点依次作为向量起始点来判断,如果有出现叉积为0的情况,则点在该线段所在直线上,再用点乘再次判断即可点是否在另一条线段上
只要出现一个点在另一线段上,即可判断为非规范相交;
如果所有叉乘结果显示一线段两段点都在另一线段的两边,即可判断为规范相交
否则判断为不相交
如果判断结果为规范相交,则可计算出交点
计算方法有很多,这里 基于重心坐标法和平行四边形面积的性质 可以求坐标
有两条线段 AB 和 CD,它们的交点为 E
将AB和CD参数化
C' = C + t * (D - C)
A' = A + s * (B - A)
现在要找到 t 和 s 的值,使得 C' = A',即E点位置。
C + t * (D - C) = A + s * (B - A)
t的值表示了线段 CD 上的点 C' 在线段上的位置关系,而 s 的值表示了线段 AB 上的点 A' 在线段上的位置关系。
重心坐标法中可以用权重来表示这些位置关系。
现在考虑两个权重,一个用来表示 C' 相对于线段 CD 的位置,另一个用来表示 A' 相对于线段 AB 的位置。我们将这两个权重分别记为 w1 和 w2。
根据平行四边形面积的性质,我们可以使用叉积来表示面积。这样可以得到以下关系:
w1 = |det(C - A, B - A)|
w2 = |det(D - A, B - A)|
现在可以使用这些权重来计算交点 E 的坐标。E 的 x 坐标可以通过以下公式计算:
E.x = (w1 * D.x + w2 * C.x) / (w1 + w2)
E.y = (w1 * D.y + w2 * C.y) / (w1 + w2)
AC代码
#include <bits/stdc++.h>
using namespace std;
const double PRECISION = 1e-8;
class Point
{
public:
double x, y;
};
int dblcmp(double d)
{
return (fabs(d) < PRECISION) ? 0:(d>0 ? 1:-1);
}
double dotdet(double x1, double y1, double x2, double y2)
{
return x1*x2 + y1*y2;
}
double det(double x1, double y1, double x2, double y2)
{
return x1*y2 - x2*y1;
}
int cross(const Point &a, const Point &c, const Point &d)
{
return dblcmp( det(a.x-c.x, a.y-c.y, d.x-c.x, d.y-c.y) );
}
bool between(const Point &a, const Point &c, const Point &d)
{
return dblcmp( dotdet(c.x-a.x, c.y-a.y, d.x-a.x, d.y-a.y) ) != 1;
}
int segIntersect(const Point &a, const Point &b, const Point &c, const Point &d)
{
int a_cd = cross(a,c,d);
if(a_cd == 0 && between(a,c,d))return 2;
int b_cd = cross(b,c,d);
if(b_cd == 0 && between(b,c,d))return 2;
int c_ab = cross(c,a,b);
if(c_ab == 0 && between(c,a,b))return 2;
int d_ab = cross(d,a,b);
if(d_ab == 0 && between(d,a,b))return 2;
if ((a_cd ^ b_cd) == -2 && (c_ab ^ d_ab) == -2) // 所有 结果为-1 和 1
return 1;
return 0;
}
void GetPoint(const Point &a, const Point &b, const Point &c, const Point &d, Point &e)
{
double sc, sd;
sc = fabs( det(b.x-a.x, b.y-a.y, c.x-a.x, c.y-a.y) );
sd = fabs( det(b.x-a.x, b.y-a.y, d.x-a.x, d.y-a.y) );
e.x = (sc * d.x + sd * c.x) / (sc + sd);
e.y = (sc * d.y + sd * c.y) / (sc + sd);
}
void solve()
{
Point a{},b{},c{},d{},ans{};
cin>>a.x>>a.y>>b.x>>b.y;
cin>>c.x>>c.y>>d.x>>d.y;
int temp=segIntersect(a,b,c,d);
if(!temp)cout<<"NO"<<endl;
else if(temp==2)cout<<"YES"<<endl;
else
{
GetPoint(a,b,c,d,ans);
cout<<fixed<<setprecision(3)<<"YES ("<<ans.x<<","<<ans.y<<")"<<endl;
}
}
signed main()
{
int _=1;
cin>>_;
while(_--)
{
solve();
}
}