POJ

来源:互联网 发布:方舟优化补丁有用吗 编辑:程序博客网 时间:2024/06/16 05:23

题目链接



感谢大佬!

感谢大佬!


判断两线段是否相交:

  1. 快速排斥
  2. 跨立实验(这两个词也是我看博客的时候看到的,觉得挺高大上的就拿过来用了,哈哈哈)

 

  1. 快速排斥:就是初步的判断一下,两条线段是不是相交,以两条线段为对角线的矩形,如果不重合的话,那么两条线段一定不可能相交。看下图:

                       

    1.线段ab的低点低于cd的最高点(可能重合)

    2.cd的最左端小于ab的最右端(可能重合)

    3.cd的最低点低于ab的最高点(加上条件1,两线段在竖直方向上重合)

    4.ab的最左端小于cd的最右端(加上条件2,两直线在水平方向上重合)

    综上4个条件,两条线段组成的矩形是重合的

    用代码实现(c++):

if(min(a.x,b.x)<=max(c.x,d.x) && min(c.y,d.y)<=max(a.y,b.y)&&min(c.x,d.x)<=max(a.x,b.x) && min(a.y,b.y)<=max(c.y,d.y))   return true;

 

  1. 跨立实验:如果两条线段相交,那么必须跨立,就是以一条线段为标准,另一条线段的两端点一定在这条线段的两段

  也就是说a b两点在线段cd的两端,c d两点在线段ab的两端

  这里就用到了向量X乘的知识点,有向量X乘的物理意义知:AB x CD=-CD x AB

  看下图:

 

  (ca x cd)·(cb x cd)<=0 则说明ca cb先对于cd的方向不同,则a b在线段cd的两侧,由此可以判断其他点


第1 步:快速排斥试验,如果分别以P1P2 ,P3P4 为对角线做矩形,而这两个矩形不相交,则这两条线段肯定不相交,如下左图;即使两个矩形相交,这两条线段也不一定相交如下右图,这时再用第步判断;

                                     

 

        表示成语句,即两个矩形相交当且仅当下列式子为真:

(max(x1,x2)≥min(x3,x4))∧ (max(x3,x4)≥min(x1,x2)) ∧(max(y1,y2)≥min(y3,y4))∧(max(y3,y4)≥min(y1,y2))

两个矩形相交必须在两个方向上都相交,式子的前半部分判断在x 方向上是否相交,后半部分判断在y 方向上是否相交。

      第2 步:确定每条线段是否“跨立”另一条线段所在的直线。

跨立:如果点P1 处于直线P3P4的一边,而P2处于该直线的另一边,则我们说线段p1p2跨立直线P3P4,如果P1 或P2 在直线P3P4 上,也算跨立。

两条线段相交当且仅当它们能够通过第1 步的快速排斥试验,并且每一条线段都跨立另一条线段所在的直线。

                      

    具体第2 步的实现,只要用叉积去做就可以了,即只要判断矢量p1p3p1p4是否在p1p2的两边相对的位置上如果这样,则线段p1p2跨立直线P3P4。也即检查叉积(P3-P1)×(P2-P1)与(P4-P1)×(P2-P1)的符号是否相同,相同则不跨立,线段也就不相交,否则相交。

当然也有一些特殊情况需要处理,如任何一个叉积为0,则P3 或P4 在直线P1P2 上,又因为通过了快速排斥试验,所以下图左边的情况是不可能出现的,只会出现右边的两种情况。当然,还会出现一条或两条线段的长度为0,如果两条线段的长度都是0,则只要通过快速排斥试验就能确定;如果仅有一条线段的长度为0,如p3p4的长度为0,则线段相交当且仅当叉积(P3-P1)×(P2-P1)。

         

有关于叉积的概念:

矢量的矢量积(叉乘、叉积)

① 矢量积的一般含义:两个矢量a 和b 的矢量积是一个矢量,记作a×b,其模等于由a 和b作成的平行四边形的面积,方向与平行四边形所在平面垂直,当站在这个方向观察时,a 逆时针转过一个小于π的角到达b 的方向。这个方向也可以用物理上的右手螺旋定则判断:右手四指弯向由A 转到B 的方向(转过的角小于π),拇指指向的就是矢量积的方向。如下图(左)。

                                

② 我们给出叉积的等价而更有用的定义,把叉积定义为一个矩阵的行列式:

           

                                     

如上右图,如果  p1 × p2 为正数,则相对原点(0,0)来说, p1 p 2 的顺时针方向; 如果p 1  × p2为负数,则p 1 在p 2 的逆时针方向。如果p 1× p =0,则p 1和p 2 模相等且共线,方向相同或相反。

 

③ 给定两个矢量:P0P1和P0P2,对它们的公共端点P0来说,判断P0P1是否在P0P2的顺时针方向。

                                         

方法:如上图,把作为原点,得出向量P1’=P1-P0 和P2’=P2-P0,因此,这两个向量的叉积为: 果该叉积为正,则P0P1P0P2的顺时针方向如果为负,则P0P1P0P2的逆时针方向如果等于0,则P0P1P2三点共线。

④ 讨论另一个重要问题:确定连续线段是向左转还是向右转,如下图,即两条连续线段P0P1

P1P2在点P1 是向左转还是向右转。也即∠P1P0P2的转向。

方法:叉积,同上。


这道题还要注意一点:

还有一些情况要考虑:





对于这个题目,如果要判断传递性可以用并查集,判断两直线相交的话就直接 快速排斥+跨立实验.


但是我对于大佬们判断以两个线段为对角线矩形相离,有我自己的理解. 我一直用的就是  

可以确定两个矩阵的左下角和右上角的点, 设分别为(x1,y1)(x2,y2)和(x3,y3)(x4,y4)

那么只要保证:if(x3>x2||x1>x4||y1>y4||y3>y2) 一定相离


#include<iostream>#include<stdio.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=50;int pre[maxn];struct node{int x,y;}q[maxn],qq[maxn];void init(){for(int i=0;i<maxn;i++)pre[i]=i;return ;}int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}//如果P1XP2>0 相对于原点来说P1在P2的顺时针方向,<0在逆时针方向,=0共线. int cmul(node q,node qq,node p){return (p.x-q.x)*(qq.y-q.y)-(qq.x-q.x)*(p.y-q.y);}int solve(node q1,node qq1,node q2,node qq2){int x1=min(q1.x,qq1.x),y1=min(q1.y,qq1.y);int x2=max(q1.x,qq1.x),y2=max(q1.y,qq1.y);int x3=min(q2.x,qq2.x),y3=min(q2.y,qq2.y);int x4=max(q2.x,qq2.x),y4=max(q2.y,qq2.y);//快速排斥.以该线段为对角线构成的两个矩形相离则一定不相交. if(x3>x2||x1>x4||y1>y4||y3>y2)return 0;//跨立实验.//必须满足 (caxcd)*(cbxcd)<=0 &&(acxab)*(adxab)<=0if(cmul(q1,qq1,q2)*cmul(q1,qq1,qq2)<=0&&cmul(q2,qq2,q1)*cmul(q2,qq2,qq1)<=0)return 1;return 0;}int n,a,b;int main(){while(~scanf("%d",&n)){init();if(n==0)break;for(int i=1;i<=n;i++)scanf("%d %d %d %d",&q[i].x,&q[i].y,&qq[i].x,&qq[i].y);for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){if(solve(q[i],qq[i],q[j],qq[j])){int f1=find(i);int f2=find(j);if(f1!=f2)pre[f1]=f2;}}}while(scanf("%d %d",&a,&b)){if(a==0&&b==0)break;if(find(a)==find(b)){puts("CONNECTED");}elseputs("NOT CONNECTED");}}return 0;}



点击打开链接


原创粉丝点击