Jack Straws Poj 1127

来源:互联网 发布:淘宝网店货源哪里找 编辑:程序博客网 时间:2024/06/09 23:00

题目解法来自《挑战程序设计竞赛》3.6 与平面和空间打交道的计算几何

1.题目原文

原题链接:http://poj.org/problem?id=1127

Jack Straws
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 4137 Accepted: 1879

Description

In the game of Jack Straws, a number of plastic or wooden "straws" are dumped on the table and players try to remove them one-by-one without disturbing the other straws. Here, we are only concerned with if various pairs of straws are connected by a path of touching straws. You will be given a list of the endpoints for some straws (as if they were dumped on a large piece of graph paper) and then will be asked if various pairs of straws are connected. Note that touching is connecting, but also two straws can be connected indirectly via other connected straws.

Input

Input consist multiple case,each case consists of multiple lines. The first line will be an integer n (1 < n < 13) giving the number of straws on the table. Each of the next n lines contain 4 positive integers,x1,y1,x2 and y2, giving the coordinates, (x1,y1),(x2,y2) of the endpoints of a single straw. All coordinates will be less than 100. (Note that the straws will be of varying lengths.) The first straw entered will be known as straw #1, the second as straw #2, and so on. The remaining lines of the current case(except for the final line) will each contain two positive integers, a and b, both between 1 and n, inclusive. You are to determine if straw a can be connected to straw b. When a = 0 = b, the current case is terminated. 

When n=0,the input is terminated. 

There will be no illegal input and there are no zero-length straws. 

Output

You should generate a line of output for each line containing a pair a and b, except the final line where a = 0 = b. The line should say simply "CONNECTED", if straw a is connected to straw b, or "NOT CONNECTED", if straw a is not connected to straw b. For our purposes, a straw is considered connected to itself.

Sample Input

71 6 3 3 4 6 4 9 4 5 6 7 1 4 3 5 3 5 5 5 5 2 6 3 5 4 7 2 1 4 1 6 3 3 6 7 2 3 1 3 0 020 2 0 00 0 0 11 12 21 20 00

Sample Output

CONNECTED NOT CONNECTED CONNECTED CONNECTED NOT CONNECTED CONNECTEDCONNECTEDCONNECTEDCONNECTED

Source

East Central North America 1994

2.题目的大致意思

桌子上放着n根木棍,木棍i的两端的坐标分别是(p[ix],p[iy])、(q[ix],q[iy])。给定m对木棍(a[i],b[i]),请判断每对木棍是否相连。当两根木棍之间有公共点时,就认为它们是相连的。通过相连的木棍间接地连在一起,也认为是相连的。
0<=m<=10000(原题似乎没给出这个数据范围……)

3.解法与思路分析

木棍就是二维平面上的线段,只要能够判断线段是否相交,那么建图之后就可以轻松判断连接性。如何判断两条线段是否相交?首先会想到计算两条直线的交点,然后再判断 交点是否在线段上这一方法。但是在几何中,运用向量的内积和外积进行计算非常方便。对于二维向量p1=(x1,y1),p2=(x2,y2),定义内积p1*p2=x1*x2+y1*y2,外积p1×p2=x1*y2-x2*y1.要判断点q是否在线段p1-p2上,只要先利用(p1-q)×(p2-q)=0判断点q在直线p1-p2上,然后再用内积(p1-q)*(p2-q)<=0来判断点q是否落在p1和p2之间。而要求两直线的交点,通过变量t将直线上的点表示为p1+t(p2-p1),交点又在直线q1-q2上,则有(q2-q1)×(p1+t(p2-p1)-q1)=0。则很容易求出交点。

注意边界情况:平行直线没有交点,平行的线段却有可能交点,此时需要特别注意。这时我们通过检查端点是否在另一条线段上判断。

4.AC代码

#include <iostream>#include<cmath>#include<cstdio>using namespace std;#define EPS 1e-10#define maxn 13#define maxm 10005//考虑误差的加法计算double add(double a,double b){    if(abs(a+b)<EPS*(abs(a)+abs(b))) return 0;    return a+b;}//二维向量结构体struct P{    double x,y;    P(){}    P(double x,double y):x(x),y(y){    }    P operator + (P p){        return P(add(x,p.x),add(y,p.y));    }    P operator - (P p){        return P(add(x,-p.x),add(y,-p.y));    }    P operator * (double d){        return P(x*d,y*d);    }    double dot(P p){        //内积,用来判断点是否在线段上        return add(x*p.x,y*p.y);    }    double det(P p){        //外积,用来判断直线是否平行        return add(x*p.y,-y*p.x);    }};//判断点q是否在线段p1-p2上bool on_seg(P p1,P p2,P q){    return (p1-q).det(p2-q)==0&&(p1-q).dot(p2-q)<=0;}//计算直线p1-p2和q1-q2的交点P intersection(P p1,P p2,P q1,P q2){    return p1+(p2-p1)*((q2-q1).det(q1-p1)/(q2-q1).det(p2-p1));}int n;P p[maxn],q[maxn];int m;int a[maxm],b[maxm];bool g[maxn][maxn];//相连关系图void solve(){    for(int i=0;i<n;i++){        g[i][i]=true;        for(int j=0;j<i;j++){            //判断木棍i与j是否有交点            if((p[i]-q[i]).det(p[j]-q[j])==0){                //平行时                g[i][j]=g[j][i]=on_seg(p[i],q[i],p[j])                                ||on_seg(p[i],q[i],q[j])                                ||on_seg(p[j],q[j],p[i])                                ||on_seg(p[j],q[j],q[i]);            }            else{                //不平行时                P r=intersection(p[i],q[i],p[j],q[j]);                g[i][j]=g[j][i]=on_seg(p[i],q[i],r)&&on_seg(p[j],q[j],r);            }        }    }    //通过Floyd-Warshall算法判断任意两点是否相连    for(int k=0;k<n;k++){        for(int i=0;i<n;i++){            for(int j=0;j<n;j++){                g[i][j]|=g[i][k]&&g[k][j];            }        }    }    for(int i=0;i<m;i++){        puts(g[a[i]-1][b[i]-1]?"CONNECTED":"NOT CONNECTED");    }}int main(){    while(cin>>n&&n){        for(int i=0;i<n;i++){            cin>>p[i].x>>p[i].y;            cin>>q[i].x>>q[i].y;        }        int s,t;        int i=0;        while(cin>>s>>t&&!(s==0&&t==0)){            a[i]=s;            b[i]=t;            i++;        }        m=i;        solve();    }    return 0;}

5.特别注意

处理误差是一个非常复杂且深奥的问题。在处理double之类的浮点数时,需要注意浮点误差。浮点数将数字表示为(尾数)*2^(指数)的形式,对于一定范围内的整数,可以精确表示,但是对于0.1这样简单的小数,却无法精确表示,只能用最接近的数近似表示。近似表示的误差称为舍入误差。比较包含舍入误差时所采用的方法,一般是选取合适的足够小的常数EPS,按如下规则处理
a<0→ a<-EPS,a<=0→ a<EPS,a==0→ abs(a)<EPS。

0 0
原创粉丝点击