• 判断两线段是否相交——快速排斥与跨立实验


      如何判断两条线段是否相交呢?如果是我们去解决这个问题,用眼睛很容易就看出来了,但是如果用计算机来解决这个问题,该怎么办呢?下面介绍两个方法,这两个方法结合起来就能完美解决这个问题了。
     
    一、快速排斥

    对于两条线段,我们以这两条线段为对角线各自作一个矩形,如图所示,如果这两个矩形没有相交的部分那么这两条线段一定不相交,这样我们可以排除一部分不相交的情况了。

    那么又该怎么判断这两个矩形是否相交呢?这就比判断线段要简单的多了,若:
    ·线段1下面的端点高于线段2上面的端点;
    ·线段1上面的端点低于线段2下面的端点;
    ·线段1左面的端点位于线段2右面的端点的右边;
    ·线段1右面的端点位于线段2左面的端点的左边;
    那么我们就可以说这两个矩形不相交,即这两个线段不相交,用代码实现如下:
    1 bool quick_judge(point a,point b,point c,point d)
    2 {
    3     if(a.y>c.y||b.y<d.y||a.x>d.x||b.x<c.x)
    4         return false;
    5     else return true;
    6 }

    但是仅这一种判断方式无法解决我们的问题,有反例如下图,两矩形相交但是线段并没有相交,这就需要我们用第二种方法来加以辅助。

    二、跨立实验
     
    首先简单介绍一下向量的叉乘:
     
      假设有两个二维向量a,b,那么它们的的叉乘结果为a×b=a.x*b.y-b.x*a.y,我们可以通过这个值得到很多有用的性质:
      ·a,b向量构成的平行四边形的面积。
      ·如果k>0时,那么a正旋转到b的角度为<180°,如果k<0,那么a正旋转到b的角度为>180°,如果k=0 那么a,b向量平行。
     
    跨立实验用到的就是上面的第二个性质。
    我们再来看跨立实验,简单来说,就是两条相交线段,其中一条的两个点一定在另一条的两边,如下图。

    那么如何去判断呢,这就需要用到我们上面说到的叉乘了。如在下图中,我们选择线段AB为基准,然后去判断AC×AB与AD×AB是否是同向的,若不是同向的,则证明了B,D两点在线段AB的两边。即 

    用代码来实现:
     1 bool cross_judge(point a,point b,point c,point d)
     2 {
     3     const double eps=1e-9;
     4     double ac,ad,cb,ca;
     5     ac=(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x);
     6     ad=(d.x-a.x)*(b.y-a.y)-(d.y-a.y)*(b.x-a.x);
     7     ca=(a.x-c.x)*(d.y-c.y)-(a.y-c.y)*(d.x-c.x);    //保险起见,把另一条边也判断一下
     8     cb=(b.x-c.x)*(d.y-c.y)-(b.y-c.y)*(d.x-c.x);
     9     if(ac*ad<eps&&ca*cb<eps) return true;
    10     else return false;
    11 }

    至此,我们将这两个方法结合一下,就可以解决我们的问题了。

    三、相关题目
     
    1.例题 ZOJ P1648
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const double eps=1e-9;
     6 
     7 struct segment
     8 {
     9     double x1,x2,y1,y2;
    10 }seg[2005];
    11 
    12 bool judge(segment a,segment b)
    13 {
    14     if(min(a.x1,a.x2)<max(b.x1,b.x2)&&max(a.x1,a.x2)>min(b.x1,b.x2)&&min(a.y1,a.y2)<max(b.y1,b.y2)&&max(a.y1,a.y2)>min(b.y1,b.y2))
    15     {
    16         double v1,v2,v3,v4;
    17         v1=(b.x1-a.x1)*(a.y2-a.y1)-(b.y1-a.y1)*(a.x2-a.x1);
    18         v2=(b.x2-a.x1)*(a.y2-a.y1)-(b.y2-a.y1)*(a.x2-a.x1);
    19         v3=(a.x1-b.x1)*(b.y2-b.y1)-(a.y1-b.y1)*(b.x2-b.x1);
    20         v4=(a.x2-b.x1)*(b.y2-b.y1)-(a.y2-b.y1)*(b.x2-b.x1);
    21         if(v1*v2<eps&&v3*v4<eps) return true;
    22         else return false;
    23     }
    24     else return false;
    25 }
    26 
    27 int main()
    28 {
    29     int i,j,n,flag=0;
    30     while(~scanf("%d",&n))
    31     {
    32         flag=0;
    33         for(i=0;i<n;i++) scanf("%lf%lf%lf%lf",&seg[i].x1,&seg[i].y1,&seg[i].x2,&seg[i].y2);
    34         for(i=0;i<n;i++)
    35             for(j=i+1;j<n;j++)
    36             {
    37                 if(judge(seg[i],seg[j]))
    38                 {
    39                     flag=1;
    40                     break;
    41                 }
    42             }
    43         if(flag) printf("burned!
    ");
    44         else printf("ok!
    ");
    45     }
    46     return 0;
    47 }
    ZOJ P1648
     
    Author : Houge  Date : 2019.5.31
    Update log : 
  • 相关阅读:
    网页连接无法打开可运行
    SET XACT_ABORT
    用Windows Live Writer写CSDN博客的步骤
    ATO,PTO
    什么是贸易顺差?
    ATO/MTO类机械制造业特点以及ERP需求分析(二)
    如何在早期版本的 Office 中打开并保存 Word 2007、Excel 2007 和 PowerPoint 2007 文件
    Alpha和Beta测试简介
    IIS6.0下 Asp网页访问出现500的问题
    合格的程序员
  • 原文地址:https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10939868.html
Copyright © 2020-2023  润新知