1、西南交通大学计算机与通信工程学院 2005年1月,程序设计基础算法,Intersecting Lines,We all know that a pair of distinct points on a plane defines a line and that a pair of lines on a plane will intersect in one of three ways: 1) no intersection because they are parallel, 2) intersect in a line because they are on top of one anoth
2、er (i.e. they are the same line), 3) intersect in a point. In this problem you will use your algebraic knowledge to create a program that determines how and where two lines intersect.,Your program will repeatedly read in four points that define two lines in the x-y plane and determine how and where
3、the lines intersect. All numbers required by this problem will be reasonable, say between -1000 and 1000. Input The first line contains an integer N between 1 and 10 describing how many pairs of lines are represented. The next N lines will each contain eight integers. These integers represent the co
4、ordinates of four points on the,plane in the order x1y1x2y2x3y3x4y4. Thus each of these input lines represents two lines on the plane: the line through (x1,y1) and (x2,y2) and the line through (x3,y3) and (x4,y4). The point (x1,y1) is always distinct from (x2,y2). Likewise with (x3,y3) and (x4,y4).
5、Output There should be N+2 lines of output. The first line of output should read INTERSECTING LINES OUTPUT. There will then be one line of output for each pair of planar lines represented by a line of input, describing how the lines intersect: none, line, or point. If the intersection is a point the
6、n,your program should output the x and y coordinates of the point, correct to two decimal places. The final line of output should read END OF OUTPUT“. Sample Input 5 0 0 4 4 0 4 4 0 5 0 7 6 1 0 2 3 5 0 7 6 3 -6 4 -3 2 0 2 27 1 5 18 5 0 3 4 0 1 2 2 5,Sample Output INTERSECTING LINES OUTPUT POINT 2.00
7、 2.00 NONE LINE POINT 2.00 5.00 POINT 1.07 2.20 END OF OUTPUT,计算几何学算法设计,1、确定任意两条线段是否都不相交设有一组线段,要求判断这组线段中任意两条线段是否都不相交。 问题分析:对于一组线段,我们可以先假设这组线段中不存在与垂直轴平行的线段,同时也没有三条线段相交于一点的情况。基于这种假设,我们可以通过一根垂直扫描线来扫描这组线段,在扫描过程中我们可以发现,一旦两条线段存在交点,则其扫描的结果会发生变化。即垂直扫描线自上而下扫描时的输,出会发生变化,如下图所示。,A,B,C,D,A,BA,BAC,DABC,ADCB,DCB,C
8、D,C,从上面的图可以看出,如果两条线段之间存在交点,则在交点前后两点之间的顺序将发生变化。因此我们可以通过如下算法来判断哪些线段之间存在交点:1、将所有线段的端点按照X坐标从小到大的顺序进行排列,如果两个端点的X坐标相同,则将Y坐标小的放在前面,Y坐标大的放在后面。设该集合为S。2、取S集合中的一个端点p。如果p是线段s的左端点,则将其插入序列T中;否则将其从序列中删除。插入的方法是:若p在T集合中某线段的上方则插入到该线段的前面,否则查找下一条线段。,这儿如何判断在p点在T集合某线段的上方呢? 我们看一下下面的图形:,P1(X1,Y1),P2(X2,Y2),P0(X0,Y0),P0P1P2
9、所组成的四边形中P0P2在P0P1的逆时针方向,P0P1P2所组成的四边形中P0P2在P0P1的顺时针方向,P1(X1,Y1),P2(X2,Y2),P0(X0,Y0),P01(X0,Y0),P01(X0,Y0),若p点在线段的上方,则表现为上图的左边的情况,否则为右图所示情况。这样就只需判断新插入的线段的左端点与T序列中的线段的左端点和右端点之间形成的线段是由逆时针旋转得到还是由顺时针旋转得到了。如何判断是顺时针还是逆时针呢?设新插入线段的左端点的坐标为P0(X0,Y0),T序列中线段的左端点坐标为P1(X1,Y1),右端点的坐标为P2(X2,Y2),考察下面的情况:,因为:tgbtga 所以
10、:(Y2-Y0)/(X2-X0)-(Y1-Y0)/(X1-X0)0 或:(Y2-Y0)(X1-X0)-(Y1-Y0)(X2-X0)0则P0P2在P0P1的逆时针方向上,否则在顺时针方向上。因此顺时针和逆时针的判断可通过叉乘的方式进行判断。 (下载源码就到源码网:),插入序列T后,如果其存在左邻结点或右邻结点,则判断其是否与其左邻结点或右邻结点相交,如果相交则返回True,程序运行结束。否则取S集合中的下一个点,作同样的处理。如果p是s的右端点,则看T序列中与该线段相邻的两条线断是否相交,如果相交,则返回True, 退出程序,否则继续处理。如下图所示。,A,B,C,D,T集合的变化过程如下:A
11、A,B A,C,B A,D,C,B ,然后到右端点,删除C,由于DB相交因此不存在完全不相交的情况,返回True。,又如下图所示:,A,B,C,D,T集合的变化过程如下:A A,B A,C,B A,D,C,B ,然后到右端点,删除C,判断DB是否相交,删除D,判断AB是否相交,删除B和A。由于没有返回True,因此返回False,存在完全不相交的情况。,在上面的算法中,涉及到如何判断两条线段是否相交的问题。那么如何判断两条线段是否相交呢?设有两条线段p1p2和p3p4,要求判断这两条线段是否相交?,要确定两条线段是否相交,可通过如下两个步骤来实现,第一步通过排斥试验如果能够确定两条线段不存在相
12、交的可能,则这两条线段不可能相交。如果通过排斥试验不能确定两条线断不相交,则第二步通过跨立实验来判断一条线段的两个端点是否处于另一条线段的两端,或者是否一条线段的一个端点在另一条线段上。如果上述情况存在,则相交。对于第一步,我们可以用如下的图形形式来描述。,从上面的图中我们可以看到两条直线并不相交,那么上面的两条线段满足什么样的条件呢?显然,满足如下两个条件1、Max(X1,X2)=Min(X3,X4)且Max(X3, X4)=Min(X1,X2) 不成立2、Max(Y1,Y2) =Min(Y3,Y4)且Max(Y3,Y4) =Min(Y1,Y2)不成立。如果上述两个条件同时成立,则说明P1P
13、2 和P3P4 不相交。相反如果不满足上述条件或者条件之一,不等于就相交,也存在不相交的可能,因此需要通过第二步跨立试验来验证两条直线是否相交。,对于第二步,我们需要判断某条直线的两个端点是否在另一条直线的两端,或者是否有一条直线的某一个端点在另一条直线上。如下图所示。,P1(X1,Y1),P3(X3,Y3),P4(X4,Y4),P2(X2,Y2),如果P1P3 或P2P3 通过相同的旋转方向可得到P1P4 或P2P4,这说明二者相交,否则不相交。其相关的表达式在前面已经得到。,P1(X1,Y1),P4(X4,Y4),P2(X2,Y2),P3(X3,Y3),很显然,如何判断两条直线是否相交可通
14、过上述方法得到。如果在上述操作结束之后,返回的是True,则说明在这组直线中存在相交的情况,如果返回False, 则说明这组直线中所有的直线均不相交。实例(浙大1280)分析: 该例子要求用户首先判断两条线段是否存在交点,然后判断一条线段是否和第二条线断重合,最后在相交的情况下求其交点。因此可以通过下面的函数实现。1、判断两条线断是否相交 int IsIntersect(pLineStr L1, pLineStr L2);函数返回2:两条线断相交,函数返回1:两条线段重合函数返回0:两条线段不相交 2、求两条线段相交的交点:pPointStr InterSect(pLineStr L1, pL
15、ineStr L2)函数返回两条线段相交的交点。 上面的函数需要定义两个结构:(1)线段的结构LineStr。定义如下typedef struct LineStrstruct PointStr sp; /线段起点struct PointStr ep; /线段终点 LineStr, *pLineStr;,点的结构PointStr,定义如下:typedef struct PointStrfloat x; /X 坐标float y; /Y坐标PointStr, *pPointStr;由于程序结构比较简单,因此在此不画其流程框图。,int IsIntersect(pLineStr L1,pLineSt
16、r L2) float x12,y12,x34,y34,x13,y13; y12=L1-ep.y-L1-sp.y;x12=L1-ep.x-L1-sp.x;y34=L2-ep.y-L2-sp.y;x34=L2-ep.x-L2-sp.x;x13=L2-sp.x-L1-sp.x;y13=L2-sp.y-L1-sp.y;,if(x34*y12=x12*y34)if(x13*y34=y13*x34) /Intersection ,pPointStr InterSect(pLineStr L1,pLineStr L2) PointStr p;float x12,x34,y12,y34,tx,ty,mx,m
17、y;x12=L1-ep.x-L1-sp.x;x34=L2-ep.x-L2-sp.x;y12=L1-ep.y-L1-sp.y;y34=L2-ep.y-L2-sp.y;,if(x12=0)p.x=L1-ep.x;if(y34=0)p.y=L2-ep.y; elsep.y=y34*(p.x-L2-sp.x)/x34+L2-sp.y;return ,if(y12=0)p.y=L1-ep.y;if(x34=0)p.x=L2-sp.x;elsep.x=(p.y-L2-sp.y)*x34/y34+L2-sp.x;return ,ty=x34*x12*L2-sp.y+y12*x34*L1-sp.x-y34*x
18、12*L2-sp.x-x34*x12*L1-sp.y;tx=x34*y12-y34*x12;p.x=ty/tx;mx=tx*x12;my=y12*(ty-tx*L1-sp.x)+tx*x12*L1-sp.y;p.y=my/mx;return ,作业,浙大网站:Circuit Board(1648),Surround the Trees,There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So at first he must know the minima
19、l required length of the rope. However, he does not know how to calculate it. Can you help him? The diameter and length of the trees are omitted, which means a tree can be seen as a point. The thickness of the rope is also omitted which means a rope can be seen as a line.,There are no more than 100
20、trees. Input The input contains one or more data sets. At first line of each input data set is number of trees in this data set, it is followed by series of coordinates of the trees. Each coordinate is a positive integer pair, and each integer is less than 32767. Each pair is separated by blank. Zer
21、o at line for number of trees terminates the input for your program. Output The minimal length of the rope. The precision should be 10-2.,Sample Input 9 12 7 24 9 30 5 41 9 80 7 50 87 22 9 45 1 50 7 0,Sample Output 243.06,2、寻找凸包的算法,在若干个点所构成的集合中,求其凸包上的点集。可采用下面的方法来求解:1、Graham扫描法(1)在点集中找一个最低位置的点P0,即Y坐标
22、最小的点,该点一定在所要找的凸包上的点。如果有多个这样的点,则找最左边的点。将所有定点与P0点之间的夹角按从小到大的顺序排列,可得到定点的序列。夹角大小的判断可通过叉乘结果进行判断,如下所示。,(Pi-P0)*(Pj-P0)如果结果大于0,则Pi线与Pj扫描,Pi排在Pj的前面。如果等于0,则取距离P0 最远的点。,P0,(2)设置一个堆栈,首先将P0,P1,P2压入堆栈,按照前面得到的序列点依次入栈,如果入栈后栈顶的元素是凸包上的点,则它应该向左转指向下一个待扫描的定点,否则就不是凸包上的顶点,应该从栈中弹出。在弹出所有的栈顶非凸包上的定点之后,再将当前的定点入栈,再取下一个扫描点,重复上述
23、操作直到处理完所有的点序列为止。这样保留在栈中的元素就是凸包上的定点。如下图所示的定点集及堆栈的变化情况。,P0,P0,P1,P2,P3,P4,P5,如何判断是向左转还是向右转呢?实际上可通过如下的公式判断:(Pi-PTop-1)*(PTop-PTop-1)如果小于0说明向左转,否则不满足向左转的条件,即如果:(Yi-YTop-1)*(XTop-XTop-1)-(YTop-YTop-1)*(Xi-XTop-1)0,则满足向左转的条件。2、Jarris法(1)与Graham法相同,先找一个Y值最小的点P0(Y值相同时取X值最小),同样选取一个Y值最,大的顶点PK。然后根据这两个定点来构造两个链表
24、,一个为左链,一个为右链。其构造方法如第2步。(2)将P0到PK之间的点按照逆时针方向夹角的大小排列成一个序列,构造成右链,同样按顺时针方向夹角的大小排列成一个序列,构造成左链。(3)同样在构造左链或右链的过程中,需要判断新加入的凸包点与待加入的凸包点和原已加入的凸包点之间的关系,如果该点右链中为逆时针,左链中为顺时针,则该点是新的凸包点。同样采用叉乘的方法进行判断。,算法思路: (1)首先寻找最低最左边的点 (2)对其他点进行排序 (3)采用Graham算法求解边界上的点 (4)计算周长,float Multi(Pt P1,Pt P2,Pt P0) float result;result=(
25、P1.x-P0.x)*(P2.y-P0.y)-(P1.y-P0.y)*(P2.x-P0.x);return result; ,void Sort() int i,j;for(i=2;i=num;i+)if(!Comp(Listi-1,Listi)List0=Listi;for(j=i-1;!Comp(Listj,List0);j-)Listj+1=Listj;Listj+1=List0;return; ,int Comp(Pt P1,Pt P2) float t,s1,s2,s3,s4;t=Multi(P1,P2,List1);s1=P1.x-List1.x;s2=P1.y-List1.y;s
26、3=P2.x-List1.x;s4=P2.y-List1.y;if(t0 | (t=0 ,int Graham() int i,Top;for(i=1;i=0)Top-;Top+;StkTop=i; return Top; ,其它几何运算问题,1、任意多边形面积求解问题我们都知道已知A(x1,y1)B(x2,y2)C(x3,y3)三点的面积公式为: (当三点为逆时针时为正,顺时针则为负的),对多边形A1A2A3、An(顺或逆时针都可以),设平面上有任意的一点P,则有: S(A1,A2,A3,、,An) = ABS(S(P,A1,A2) + S(P,A2,A3)+、+S(P,An,A1)) P可
27、以取任意的一点,用(0,0)也可以了。,其基本原理如下图所示。,P,A,B,C,D,E,F,G,H,判断点是否在多边形中判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点,向左方作射线L,由于多边形是有界的,所以射线L的左端一定在多边形外,考虑沿着L从无穷远处开始自左向右移动,遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边形,所以很容易看出当L和多边形的交点数目C是奇数的时候,P在多边形内,是偶数的话P在多边形外。,但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中,L和多边形的顶点相交,这时候交点
28、只能计算一个;在图(b)中,L和多边形顶点的交点不应被计算;在图(c)和(d) 中,L和多边形的一条边重合,这条边应该被忽略不计。如果L和多边形的一条边重合,这条边应该被忽略不计。,a,b,为了统一起见,我们在计算射线L和多边形的交点的时候,1、对于多边形的水平边不作考虑;2、对于多边形的顶点和L相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;3、对于P在多边形边上的情形,直接可判断P属于多边行。,C,D,可用下面的语言描述:count=0; 以P为端点,作从右向左的射线L; for (多边形的每条边s) if (P在边s上) return true; if (s不是水平的) if(s的一个端点在L上) if (该端点是s两端点中纵坐标较大的端点) count=count+1; else if ( s和L相交) count=count+1; ,if(count mod 2 = 1) return true; return false; 其中做射线L的方法是:设P的纵坐标和P相同,横坐标为负无穷大(很大的一个负数),则P和P就确定了射线L。 (下载源码就到源码网:),