POJ 1127 并查集+几何

题意:桌子上放着n根木棍,木棍的两端坐标分别是(Pix, Piy)和(Qix, Qiy)。给定m对木棍(ai, bi),请判断没对木棍是否相连。当两根木棍之间有公共点时,就认为它们是相连的。通过相连的木棍间接的连在一起的两根木棍也认为是相连的。

分析

木棍就是二维平面上的线段,只有能够判断线段是否相交,那么建图以后就可以轻松的进行连接性判断。那么,应该如何判断两条线段是否相交呢?首先会想到计算两直线的交点,然后判断交点是否在线段上这一方法。那么两条直线的交点要怎么求得呢?虽然可以把直线表示成方程,通过联立方程组求解,但是在几何问题中,运用向量的内积和外积进行计算是非常方便的。对于二维向量p1=(x1, y1) 和 p2=(x2, y2),我们定义内积p1*p2 = x1x2 +y1y2,外积p1*p2 = x1y2 – x2y1。要判断点q是否在线段p1-p2上,只有先利用外积根据是否有(p1-q)*(p2 – q)=0来判断点q是否在直线p1-p2上,在利用内积根据是否有(p1-q)*(p2– q)<=0来判断点q是否落在p1-p2之间。而要求两直线的交点,通过变量t将直线p1-p2上的点表示为p1+t(p2-p1),交点又在直线q1-q2上,所以有:

(q2-q1)*(p1+t(p2-p1)-q1) = 0

于是可以利用下式求得t的值

P1+(q2-q1)*(q1-p1)*(p2-p1)/(q2-q1)*(p2-p1)

但是,使用这个方法时还有注意边界情况。让我们来看看样例中的木棍2和木棍4,这两条线段是平行的,对应直线没有交点。但平行的线段也可能有公共点,所有此时需要特别注意。对此有不同的处理方法,这里我们选择通过检查端点是否在另一条线段上来判断。

#include <iostream>
#include <cstdio>
using namespace std;
int set[20];
struct Point
{
    int x1,x2,y1,y2;
    Point(int x1 = 0, int x2 = 0, int y1 = 0, int y2 = 0) : x1(x1),x2(x2),y1(y1),y2(y2) {};
    void read()
    {
        scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
    }
}p[20];
void unit(int n){ 
    for(int i = 1; i <= n; i++) set[i] = i;
}
int find(int x){ 
	return x == set[x] ? x : set[x] = find(set[x]);
}
int cross(int x1, int y1, int x2, int y2){ 
    return x1 * y2 - x2 * y1;
} 
int intersection(Point A, Point B) //判断直线相交
{
    int c[4];
    if(max(A.x1,A.x2) < min(B.x1,B.x2) || max(A.y1,A.y2) < min(B.y1,B.y2)
       || max(B.x1,B.x2) < min(A.x1,A.x2) || max(B.y1,B.y2) < min(A.y1,A.y2) ) return 0; //考虑共线不相交的情况,为快速排斥定理
    /*判断两条直线是否相交,即只需判断线是否在另一条线的两端*/
    c[0] = cross(A.x2 - A.x1, A.y2 - A.y1, B.x1 - A.x1, B.y1 - A.y1);
    c[1] = cross(A.x2 - A.x1, A.y2 - A.y1, B.x2 - A.x1, B.y2 - A.y1);
    c[2] = cross(B.x2 - B.x1, B.y2 - B.y1, A.x1 - B.x1, A.y1 - B.y1);
    c[3] = cross(B.x2 - B.x1, B.y2 - B.y1, A.x2 - B.x1, A.y2 - B.y1);
    if(c[0] * c[1] <= 0 && c[2] * c[3] <= 0) return 1; //运用到了向量的叉乘和点乘的知识;
    return 0;
}

int main(){ 
    int n;
    while(~scanf("%d",&n) && n){ 
        for(int i = 1; i <= n; i++)
            p[i].read();
        unit(n);
        for(int i = 1; i <= n; i++){ 
            for(int j = i + 1; j <= n; j++){ 
                if(intersection(p[i],p[j])){ 
                    int a = find(i);
                    int b = find(j);
                    if(a != b) set[a] = b;
                }
            }
        }
        int a,b;
        while(~scanf("%d %d",&a,&b), a | b){ 
            a = find(a);
            b = find(b);
            if(a == b) puts("CONNECTED");
            else puts("NOT CONNECTED");
        }
    }
    return 0;
}

参考:挑战程序设计竞赛

猜你喜欢

转载自blog.csdn.net/qq_21989927/article/details/81414619