题意:桌子上放着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;
}
参考:挑战程序设计竞赛