【深大cppOJ】线段相交-结构体

有一天,我的一个朋友给我发了这样的一道题:
图片太模糊了

题目描述:

每个线段是用平面上的两点来描述,用结构体实现对于任意输入的2个线段,判断是否相交。
提示:两点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的直线斜率为
k = ( y 2 − y 1 ) / ( x 2 − x 1 ) k=(y_2-y_1)/(x_2-x_1) k=(y2y1)/(x2x1)

输入:

3
1 5 2 9
1 3 2 4
5 6 7 8
5 7 7 7
2 5 1 0
9 4 2 9

输出:

disjoint
intersect
disjoint

和好基友讨论了一番,一共找到了三种解决这个问题的方法:
①朋友的方法:构造四边形,判断点是否在四边形内
②我的方法:假定这两条线段是直线,求出两直线交点,再判断交点是否在这两条直线内,在即相交.
③向量叉乘:利用向量叉乘的正负判断线段是否相交(但有前提条件).

我们来分别讨论这三种方法:

①朋友的方法

代码:

#include <iostream>
#include <cstdio>
using namespace std;
//结构体定义
struct point {
 double x;
 double y;
}p1,p2,p3,p4,pt,pc;
//p1和p2构成直线1;p3和p4构成直线2
int main()
{
 int n;
 cin >> n;
 while (n--) {
  cin >> p1.x >> p1.y;
  cin >> p2.x >> p2.y;
  cin >> p3.x >> p3.y;
  cin >> p4.x >> p4.y;
  double k1 = (p1.y - p2.y) / (p1.x - p2.x);	//p1p2斜率
  double k2 = (p3.y - p4.y) / (p3.x - p4.x);	//p3p4斜率
  pt.x = p4.x - p3.x;
  pt.y = p4.y - p3.y;
  
  bool temp1 = false;
  bool temp2 = false;
  bool temp3 = false;
  bool temp4 = false;
  bool ans1 = false;
  bool ans2 = false;
  //第一次判断
  if (p3.y <= k1 * (p3.x - p1.x) + p1.y) temp1 = true;
  if (p3.y >= k1 * (p3.x - p1.x + pt.x) + p1.y - pt.y) temp2 = true;
  if (p3.y <= k2 * (p3.x - p1.x) + p1.y) temp3 = true;
  if (p3.y >= k2 * (p3.x - p2.x) + p2.y) temp4 = true;
  if (temp1 && temp2 && temp3 && temp4) ans1=true;
  
  //直线点的“互换”
  pc = p1;
  p1 = p3;
  p3 = pc;
  pc = p2;
  p2 = p4;
  p4 = pc;
  k1 = (p1.y - p2.y) / (p1.x - p2.x);
  k2 = (p3.y - p4.y) / (p3.x - p4.x);
  pt.x = p4.x - p3.x;
  pt.y = p4.y - p3.y;
  temp1 = false;
  temp2 = false;
  temp3 = false;
  temp4 = false;
  //第二次判断
  if (p3.y <= k1 * (p3.x - p1.x) + p1.y) temp1 = true;
  if (p3.y >= k1 * (p3.x - p1.x + pt.x) + p1.y - pt.y) temp2 = true;
  if (p3.y <= k2 * (p3.x - p1.x) + p1.y) temp3 = true;
  if (p3.y >= k2 * (p3.x - p2.x) + p2.y) temp4 = true;
  if (temp1 && temp2 && temp3 && temp4) ans2 = true;
  //其中一次若符合就相交
  if (ans1 || ans2) cout << "intersect\n";
  else cout << "disjoint\n";
 }
 
 return 0;
}
//这个代码是我朋友写的

原理:
假设两条线段相交,那么这两条线段可以构造出四边形,
如果两条线段不相交,那么这两条线段只能构造出三角形:
在这里插入图片描述

根据画图分析可以得出:假设某点(指 p 3 p_3 p3 p 4 p_4 p4)存在,若 p 3 p_3 p3在直线 p 1 p 2 p_1p_2 p1p2的上方且 p 3 p_3 p3在虚线 p 1 p 3 p_1p_3 p1p3 p 2 p 3 p_2p_3 p2p3的下方(第一次判断),或 p 4 p_4 p4在直线 p 1 p 2 p_1p_2 p1p2的下方且 p 4 p_4 p4在虚线 p 1 p 4 p_1p_4 p1p4 p 2 p 4 p_2p_4 p2p4的上方(第二次判断),那么这两条线段就相交。(因为p3和p4的位置本身就不确定,所以要有两次判断)

我们如何判断点是否在直线上方或下方呢?直线不等式:
由上述关系可以得出:如果点满足下面的方程组
p 3 . y − p 1 . y < = k 1 ( p 3 . x − p 1 . x ) p_3.y-p_1.y<=k_1(p_3.x-p_1.x) p3.yp1.y<=k1(p3.xp1.x)
p 3 . y − ( p 1 . y − p t . y ) > = k 1 [ p 3 . x − ( p 1 . x − p t . x ) ] p_3.y-(p_1.y-p_t.y)>=k_1[p_3.x-(p_1.x-p_t.x)] p3.y(p1.ypt.y)>=k1[p3.x(p1.xpt.x)]
p 3 . y − p 1 . y < = k 2 ( p 3 . x − p 1 . x ) p_3.y-p_1.y<=k_2(p_3.x-p_1.x) p3.yp1.y<=k2(p3.xp1.x)
p 3 . y − p 2 . y > = k 2 ( p 3 . x − p 2 . x ) p_3.y-p_2.y>=k_2(p_3.x-p_2.x) p3.yp2.y>=k2(p3.xp2.x)
对于点 p 4 p_4 p4上述关系若成立也符合

②我的方法:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

struct point {
 double x;
 double y;
}p1, p2, p3, p4, pt;

int main()
{
 int n;
 cin >> n;
 while (n--) {
  cin >> p1.x >> p1.y;
  cin >> p2.x >> p2.y;
  cin >> p3.x >> p3.y;
  cin >> p4.x >> p4.y;
  double k1 = (p1.y - p2.y) / (p1.x - p2.x);
  double k2 = (p3.y - p4.y) / (p3.x - p4.x);
  if (k1 == k2 && k1 * p1.x - p1.y == k2 * p3.x - p3.y) {
  	//特殊情况的判断(即使重合但无交点的情况)
   if ((max(p1.x, p2.x) < min(p3.x, p4.x))||(max(p3.x,p4.x)<min(p1.x,p2.x))) {
    cout << "disjoint\n";
    continue;
   }
   else {
    cout << "intersect\n";
    continue;
   }
  }
  else if (k1 == k2 && k1 * p1.x - p1.y != k2 * p3.x - p3.y) {
   cout << "disjoint\n";
   continue;
  }
  else {
   pt.x = (k1 * p1.x - k2 * p3.x - p1.y + p3.y) / (k1 - k2);
   pt.y = k1 * (pt.x - p1.x) + p1.y;
   bool temp1 = false;
   bool temp2 = false;
   if (min(p1.x, p2.x) <= pt.x && pt.x <= max(p1.x, p2.x)) {
    temp1 = true;
   }
   if (min(p3.x, p4.x) <= pt.x && pt.x <= max(p3.x, p4.x)) {
    temp2 = true;
   }
   if (temp1 && temp2) {
    cout << "intersect\n";
   }
   else cout << "disjoint\n";
  }


 }

 return 0;
}
//我的思路,它写的代码

原理:
假设两条线段不相交,那么分别延长两条线段,直到相交,如图:
在这里插入图片描述

我们要判断的就是:这个交点是否在这两条线段内

所以第一步就是要求出交点坐标?
不,我们先判断两条线段的斜率是否相等?如果相等,计算两条线段的距离,如果等于0就相交,否则不相交。
如果斜率不相等,那么计算交点坐标,并判断。

具体如下:
k 1 = = k 2 k_1==k_2 k1==k2的情况
构造两线段方程:
y − p 1 . y = k 1 ( x − p 1 . x ) ( ∗ ) y-p_1.y=k_1(x-p_1.x)(*) yp1.y=k1(xp1.x)()
y − p 3 . y = k 2 ( x − p 3 . x ) ( ∗ ∗ ) y-p_3.y=k_2(x-p_3.x)(**) yp3.y=k2(xp3.x)()
联立(*)(**),且 k 1 = k 2 k_1=k_2 k1=k2,可以得到直线距离
d = ( k 2 p 3 . x − k 1 p 1 . x + p 1 . y − p 3 . y ) / ( 关 于 k 1 和 k 2 的 方 程 ) d=(k_2p_3.x-k_1p_1.x+p_1.y-p_3.y)/(关于k_1和k_2的方程) d=(k2p3.xk1p1.x+p1.yp3.y)/(k1k2)
d = 0 d=0 d=0,就得到
k 2 p 3 . x − p 3 . y = k 1 p 1 . x − p 1 . y k_2p_3.x-p_3.y=k_1p_1.x-p_1.y k2p3.xp3.y=k1p1.xp1.y
如果这个等式成立,那么这两条线段就重合(也就是相交了)
否则就不相交

k 1 ! = k 2 k_1!=k_2 k1!=k2的情况
联立(*)(**),求出交点坐标:
p t . x = [ k 1 p 1 . x − k 2 p 3 . x − ( p 1 . y − p 3 . y ) ] / ( k 1 − k 2 ) p_t.x=[k_1p_1.x-k_2p_3.x-(p_1.y-p_3.y)]/(k_1-k_2) pt.x=[k1p1.xk2p3.x(p1.yp3.y)]/(k1k2)
p t . y = k 1 ( p t . x − p 1 . x ) + p 1 . y p_t.y=k_1(p_t.x-p_1.x)+p_1.y pt.y=k1(pt.xp1.x)+p1.y

由图可以看出,若使两线段相交,只要交点x坐标大于等于min(p1.x,p2.x)且小于等于max(p1.x,p2.x)
同样,对于线段p3p4同样符合
这两种都符合线段就相交

③向量叉乘法:

#include <iostream>
#include <algorithm>
using namespace std;
//点类
class point
{
public:
    int x;
    int y;
};
//根据向量叉乘判断是否相交(跨立实验)
bool judgement(point p1, point p2, point p3, point p4)
{
    point vec_ca;
    point vec_cb;
    point vec_cd;
    vec_ca.x = p1.x - p3.x;
    vec_ca.y = p1.y - p3.y;
    vec_cb.x = p1.x - p4.x;
    vec_cb.y = p1.y - p4.y;
    vec_cd.x = p1.x - p2.x;
    vec_cd.y = p1.y - p2.y;
    int q = (vec_ca.x * vec_cd.y - vec_ca.y * vec_cd.x) * (vec_cb.x * vec_cd.y - vec_cd.x * vec_cb.y);
    if (q >= 0)
        return false;
    else
    {
        return true;
    }
}
//向量叉乘的前提条件判断(快速排斥实验)
bool projJudge(point p1, point p2, point p3, point p4)
{
    if (max(p1.x, p2.x) <= min(p3.x, p4.x)||max(p3.x,p4.x)<=min(p1.x,p2.x)||
        max(p3.y,p4.y)<=min(p1.y,p2.y)||max(p1.y,p2.y)<=min(p3.y,p4.y))
        return true;
    else
    {
        return false;
    }
    
}

int main()
{
    int n;
    point p1, p2, p3, p4;
    cin >> n;
    while (n--)
    {
        cin >> p1.x >> p1.y >> p2.x >> p2.y;
        cin >> p3.x >> p3.y >> p4.x >> p4.y;
        if (!projJudge(p1, p2, p3, p4))
        {
            if (judgement(p1, p2, p3, p4))
                cout << "intersect";
        }
        else
        {
            cout << "disjoint";
        }
        cout << endl;
    }
    return 0;
}
//我写的代码

原理:
在这里插入图片描述
①先进行快速排斥实验
如果这两条线段构成的矩形不能相交,明显就不会相交,用方程表示:
m a x ( p 1. x , p 2. x ) < = m i n ( p 3. x , p 4. x ) ∣ ∣ m a x ( p 3. x , p 4. x ) < = m i n ( p 1. x , p 2. x ) ∣ ∣ m a x ( p 3. y , p 4. y ) < = m i n ( p 1. y , p 2. y ) ∣ ∣ m a x ( p 1. y , p 2. y ) < = m i n ( p 3. y , p 4. y ) max(p1.x, p2.x) <= min(p3.x, p4.x) || max(p3.x,p4.x)<=min(p1.x,p2.x) || max(p3.y,p4.y)<=min(p1.y,p2.y) || max(p1.y,p2.y)<=min(p3.y,p4.y) max(p1.x,p2.x)<=min(p3.x,p4.x)max(p3.x,p4.x)<=min(p1.x,p2.x)max(p3.y,p4.y)<=min(p1.y,p2.y)max(p1.y,p2.y)<=min(p3.y,p4.y)
②如果通过快速排斥实验,那么进行跨立实验
在这里插入图片描述
具体可看:https://segmentfault.com/a/1190000004070478

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值