*HDU 1086 计算几何
You can Solve a Geometry Problem too
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10259 Accepted Submission(s): 5074
Give you N (1<=N<=100) segments(线段), please output the number of all intersections(交点). You should count repeatedly if M (M>2) segments intersect at the same point.
Note:
You can assume that two segments would not intersect at more than one point.
A test case starting with 0 terminates the input and this test case is not to be processed.
第1 步:快速排斥试验,如果分别以P1P2 ,P3P4 为对角线做矩形,而这两个矩形不相交,则这两条线段肯定不相交,如下左图;即使两个矩形相交,这两条线段也不一定相交,如下右图,这时再用第2 步判断;
表示成语句,即两个矩形相交当且仅当下列式子为真:
(max(x1,x2)≥min(x3,x4))&& (max(x3,x4)≥min(x1,x2)) &&(max(y1,y2)≥min(y3,y4))&&(max(y3,y4)≥min(y1,y2))
两个矩形相交必须在两个方向上都相交,式子的前半部分判断在x 方向上是否相交,后半部分判断在y 方向上是否相交。
第2 步:确定每条线段是否“跨立”另一条线段所在的直线。
跨立:如果点P1 处于直线P3P4的一边,而P2处于该直线的另一边,则我们说线段p1p2跨立直线P3P4,如果P1 或P2 在直线P3P4 上,也算跨立。
两条线段相交当且仅当它们能够通过第1 步的快速排斥试验,并且每一条线段都跨立另一条线段所在的直线。
具体第2 步的实现,只要用叉积去做就可以了,即只要判断矢量p1p3和p1p4是否在p1p2的两边相对的位置上,如果这样,则线段p1p2跨立直线P3P4。也即检查叉积(P3-P1)×(P2-P1)与(P4-P1)×(P2-P1)的符号是否相同,相同则不跨立,线段也就不相交,否则相交。
当然也有一些特殊情况需要处理,如任何一个叉积为0,则P3 或P4 在直线P1P2 上,又因为通过了快速排斥试验,所以下图左边的情况是不可能出现的,只会出现右边的两种情况。当然,还会出现一条或两条线段的长度为0,如果两条线段的长度都是0,则只要通过快速排斥试验就能确定;如果仅有一条线段的长度为0,如p3p4的长度为0,则线段相交当且仅当叉积(P3-P1)×(P2-P1)。
有关于叉积的概念:
矢量的矢量积(叉乘、叉积)
① 矢量积的一般含义:两个矢量a 和b 的矢量积是一个矢量,记作a×b,其模等于由a 和b作成的平行四边形的面积,方向与平行四边形所在平面垂直,当站在这个方向观察时,a 逆时针转过一个小于π的角到达b 的方向。这个方向也可以用物理上的右手螺旋定则判断:右手四指弯向由A 转到B 的方向(转过的角小于π),拇指指向的就是矢量积的方向。如下图(左)。
② 我们给出叉积的等价而更有用的定义,把叉积定义为一个矩阵的行列式:
如上右图,如果 p1 × p2 为正数,则相对原点(0,0)来说, p1 在p 2 的顺时针方向; 如果p 1 × p2为负数,则p 1 在p 2 的逆时针方向。如果p 1× p2 =0,则p 1和p 2 模相等且共线,方向相同或相反。
③ 给定两个矢量:P0P1和P0P2,对它们的公共端点P0来说,判断P0P1是否在P0P2的顺时针方向。
方法:如上图,把0 p 作为原点,得出向量P1’=P1-P0 和P2’=P2-P0,因此,这两个向量的叉积为: 如果该叉积为正,则P0P1在P0P2的顺时针方向,如果为负,则P0P1在P0P2的逆时针方向。如果等于0,则P0,P1,P2三点共线。
④ 讨论另一个重要问题:确定连续线段是向左转还是向右转,如下图,即两条连续线段P0P1
和P1P2在点P1 是向左转还是向右转。也即∠P1P0P2的转向。
方法:叉积,同上。
这道题还要注意一点:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 double nodx1[102],nody1[102],nodx2[102],nody2[102]; 7 int n; 8 int main() 9 { 10 while(scanf("%d",&n)&&n) 11 { 12 for(int i=0;i<n;i++) 13 scanf("%lf%lf%lf%lf",&nodx1[i],&nody1[i],&nodx2[i],&nody2[i]); 14 double x1,y1,x2,y2; 15 int ans=0; 16 for(int i=0;i<n-1;i++) 17 for(int j=i+1;j<n;j++) 18 { 19 if(!((min(nodx1[i],nodx2[i])<=max(nodx1[j],nodx2[j])) 20 &&(max(nody1[i],nody2[i])>=min(nody1[j],nody2[j])) 21 &&(min(nodx1[j],nodx2[j])<=max(nodx1[i],nodx2[i])) 22 &&(max(nody1[j],nody2[j])>=min(nody1[i],nody2[i])))) 23 continue; //快速排斥 24 x1=nodx2[i]-nodx1[i];y1=nody2[i]-nody1[i]; 25 x2=nodx2[j]-nodx1[i];y2=nody2[j]-nody1[i]; 26 double tem1=x2*y1-x1*y2; 27 x2=nodx1[j]-nodx1[i];y2=nody1[j]-nody1[i]; 28 double tem2=x2*y1-x1*y2; 29 x1=nodx2[j]-nodx1[j];y1=nody2[j]-nody1[j]; 30 x2=nodx2[i]-nodx1[j];y2=nody2[i]-nody1[j]; 31 double tem3=x2*y1-x1*y2; 32 x2=nodx1[i]-nodx1[j];y2=nody1[i]-nody1[j]; 33 double tem4=x2*y1-x1*y2; 34 if(tem1*tem2<=0&&tem3*tem4<=0) ans++; 35 } 36 printf("%d\n",ans); 37 } 38 return 0; 39 }