线段相交Ⅲ

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代码

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
#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(); } }

本文作者:Minza

本文链接:https://www.cnblogs.com/minz-io/p/17625343.html

posted @   Minza  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开