POJ 1127 基础计算几何(判断两线段相交)+并查集

来源:互联网 发布:德云社网络相声大会 编辑:程序博客网 时间:2024/05/19 20:22

传送门:POJ1127

题意:给定n条线段和多个询问(a,b),问a和b两个线段是否相交,相交的性质可以传递。

思路:相交的传递性可以用并查集来维护,关键是如何判断两个线段是否相交,大白上给出了用向量内积和外积计算的方法,不过比较麻烦,dalao们通用的方法是进行两个判断:
1.快速排斥

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

       

判断的四个条件:                

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

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

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

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

2.跨立实验

  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的两侧,由此可以判断

以上部分转自:点击打开链接

若两个判断都满足,则说明两线段相交。

代码:

#include<iostream>using namespace std;typedef pair<int,int>P;#define fi first#define se secondP p[20],q[20];//计算向量p1p2与向量p1p3的叉积,若p1p3在p1p2的逆时针方向,则返回>0,顺时针方向返回<0int mul(P p1, P p2, P p3){    return (p2.fi - p1.fi) * (p3.se - p1.se) - (p3.fi - p1.fi) * (p2.se - p1.se);}bool intersect(P p1,P p2,P q1,P q2){//快速排斥    if(max(p1.fi,p2.fi)<min(q1.fi,q2.fi)||max(q1.fi,q2.fi)<min(p1.fi,p2.fi)||max(p1.se,p2.se)<min(q1.se,q2.se)||max(q1.se,q2.se)<min(p1.se,p2.se))    return 0;//跨立试验    if(mul(p1,p2,q1)*mul(p1,p2,q2) <= 0 && mul(q1,q2,p1)*mul(q1,q2,p2) <= 0)    return 1;    return 0;}int f[100];int getf(int k){    return k==f[k]?k:f[k]=getf(f[k]);}int main(){    int n;    while(cin>>n&&n)    {        for(int i=1;i<=n;i++)        {            f[i]=i;            cin>>p[i].fi>>p[i].se>>q[i].fi>>q[i].se;        }        for(int i=1;i<=n;i++)        for(int j=1;j<i;j++)        {            if(intersect(p[i],q[i],p[j],q[j]))            {                int x=getf(i),y=getf(j);                if(x!=y)                    f[x]=y;            }        }        int l,r;        while(cin>>l>>r&&l+r)        {            if(getf(l)==getf(r)) cout<<"CONNECTED\n";            else cout<<"NOT CONNECTED\n";        }    }}



原创粉丝点击