• ZOJ 1648 Circuit Board


    计算几何(判断线段是否相交问题)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1648

    CSUST 2012年暑假8月组队后个人训练赛第11场,题目A:http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=11851#problem/A

    另外附上一个很牛逼的博客的判断线段相交问题:http://www.cnblogs.com/g0feng/archive/2012/05/18/2508293.html

    Circuit Board

    Time Limit: 2 Seconds      Memory Limit: 65536 KB

    On the circuit board, there are lots of circuit paths. We know the basic constrain is that no two path cross each other, for otherwise the board will be burned.

    Now given a circuit diagram, your task is to lookup if there are some crossed paths. If not find, print "ok!", otherwise "burned!" in one line.

    A circuit path is defined as a line segment on a plane with two endpoints p1(x1,y1) and p2(x2,y2).

    You may assume that no two paths will cross each other at any of their endpoints.


    Input

    The input consists of several test cases. For each case, the first line contains an integer n(<=2000), the number of paths, then followed by n lines each with four float numbers x1, y1, x2, y2.


    Output

    If there are two paths crossing each other, output "burned!" in one line; otherwise output "ok!" in one line.


    Sample Input

    1
    0 0 1 1

    2
    0 0 1 1
    0 1 1 0


    Sample Output

    ok!
    burned!


    题目大意:在一个电板上以坐标的形式给你N条线段,我们都知道电路中的线路是不能相交的,所以一旦遇到相交的情况就输出burned!否则输出ok!

    相关转化:说白了就是给你N条线段,让你判断给出的线段是否相交。用跨立实验直接判断即可,一旦相交就跳出循环,输出burned!否则输出ok!

    相关算法:计算几何,用跨立实验判断两线段不相交。

    关于判断两线段是否相交(思想来自大一上学期寒假学长给的计算几何资料):

    我们分两步确定两条线段是否相交:

    (1)快速排斥试验

    设以线段 P1P2 为对角线的矩形为R, 设以线段 Q1Q2 为对角线的矩形为T,如果RT不相交,显然两线段不会相交。

    (PS:我自己貌似从来没有用过这个,都是直接用的下面的跨立实验)

    (2)跨立试验

    (PS:下面的公式中*代表点积,×代表叉积

    如果两线段相交,则两线段必然相互跨立对方。

    P1P2跨立Q1Q2 ,则矢量 ( P1 - Q1 ) ( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧,

    ( P1 - Q1 ) × ( Q2 - Q1 ) * ( P2 - Q1 ) × ( Q2 - Q1 ) < 0

    上式可改写成( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) > 0

    当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时,说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2上;

    同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。

    所以判断P1P2跨立Q1Q2的依据是:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >  0

    同理判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >  0。 


    在相同的原理下,对此算法的具体的实现细节可能会与此有所不同,除了这种过程外,大家也可以参考《算法导论》上的实现。

    判断线段和直线是否相交:

    有了上面的基础,这个算法就很容易了。如果线段P1P2和直线Q1Q2相交,则P1P2跨立Q1Q2

    即:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) > 0

    上面是我看的学长给的资料,下面是我自己的解释:

    有关这个公式:( P1 - Q1 )×( Q2 - Q1 )*( Q2 - Q1 )×( P2 - Q1 ) > 0。(与上面的一样,只是我习惯了这个形式。。。)

    看懂这个公式需要掌握的数学知识:向量的叉积和向量的点积。

    向量的点积:

                     给你两个向量A(ax,by),B(bx,by).

                     向量A和B的点积公式:A*B=ax*bx+ay*by.

    向量的叉积:

                    给你两个向量A(ax,by) ,B(bx,by).

                    向量的叉积公式:A×B=ax*by-ay*bx.

                具体看下面的行列式:

                                   i        j       k

                 A×B =     ax     ay     0  =  ax*by-ay*bx   = |A|*|B|*sin<A,B>    注意:叉积的结果仍然是个向量,A×B = -B×A

                                   bx     by    0

    叉积的几何意义:

                                 右手定则:如上图所示,红色的向量表示V×U的结果。四指形成一个平面,大拇指与四指形成的平面垂直,四个指从V扫描到U大拇指的指向就是叉乘所得向量的方向。

    继续跨立解释: 如果(P1-P2)跨立(Q1-Q2)那么向量(Q1-P1)×(Q1-Q2)后的向量方向与向量(Q1-Q2)×(Q1-P1)后的向量方向的夹角一定                   小于九十度 。那么他们的点积就 >0

     

    注意:一般情况下,为了避免跨立不相交的局面,一定要判断线段P1P2跨立Q1Q2后再反过来判断Q1Q2跨立P1P2如果两者都跨立,才能断定他们相交。

    以上只是个人小结,部分东西没有提及,有些东西也说的过于啰嗦,不对之处,还望各路大神批评指正。具体的这些理论知识还是找百度较好~~~~~~

     

    下面是我自己写的一个判断两线段是否相交的模板:

    P1P2横跨Q1Q2:

                                   ( P1 - Q1 )×( Q2 - Q1 )*( Q2 - Q1 )×( P2 - Q1 ) > 0

                                  temp={ (P1.x-Q1.x)*(Q2.y-Q1.y) - (P1.y-Q1.y)*(Q2.x-Q1.x) } * {(Q2.x-Q1.x)*(P2.y-Q1.y) - (Q2.y-Q1.y)*(P2.x-Q1.x)}>0

    关于这道题目要注意的地方:题目中要求的是点的坐标是float型,开始我没注意,一直用的int,结果TLE(超时)一上午啊!!!

     

    //AC 244kb 20ms ZOJ1648 Circuit Board

    代码一:

    //AC 244kb 20ms ZOJ1648 Circuit Board
    #include<stdio.h>
    struct Line
    {
        double x1;
        double y1;
        double x2;
        double y2;
    }line[2010];
    int main()
    {
        int n;
        int i,j;
        while(scanf("%d",&n)!=EOF)
        {
            int ok=1;//判断是否有线段相交
            for(i=0;i<n;i++)
                scanf("%lf%lf%lf%lf",&line[i].x1,&line[i].y1,&line[i].x2,&line[i].y2);
    		for(i=0;i<n-1;i++)
    		{
    			for(j=i+1;j<n;j++)
    			{
    				double s1=((line[i].x1-line[j].x1)*(line[j].y2-line[j].y1)-(line[i].y1-line[j].y1)*(line[j].x2-line[j].x1))
                              *((line[j].x2-line[j].x1)*(line[i].y2-line[j].y1)-(line[j].y2-line[j].y1)*(line[i].x2-line[j].x1));
    				if(s1>0)//如果line[i]跨立line[j]下面继续判断line[j]是否跨立line[i]
    				{
    					double s2=((line[j].x1-line[i].x1)*(line[i].y2-line[i].y1)-(line[j].y1-line[i].y1)*(line[i].x2-line[i].x1))
                              *((line[i].x2-line[i].x1)*(line[j].y2-line[i].y1)-(line[i].y2-line[i].y1)*(line[j].x2-line[i].x1));
    					if(s2>0)//如果二者均跨立,那么就不用进行下面的判断,直接跳出循环
    					{
    						ok=0; 
    						break;
    					}
    				}
    			}
    			if(ok==0)  //注意一个break只能跳出一个for循环
    				break;
    		}
    	    if(ok==1)
    			printf("ok!\n");
    		else
    			printf("burned!\n");
    
        }
        return 0;
    }


     

      


     

    代码二:(想一步跳出循环用goto写的)

    #include<stdio.h>
    struct Line
    {
        double x1;
        double y1;
        double x2;
        double y2;
    }line[2010];
    int main()
    {
        int n;
        int i,j;
        while(scanf("%d",&n)!=EOF)
        {
            int ok=1;
            for(i=0;i<n;i++)
                scanf("%lf%lf%lf%lf",&line[i].x1,&line[i].y1,&line[i].x2,&line[i].y2);
    		for(i=0;i<n-1;i++)
    		{
    			for(j=i+1;j<n;j++)
    			{
    				double s1=((line[i].x1-line[j].x1)*(line[j].y2-line[j].y1)-(line[i].y1-line[j].y1)*(line[j].x2-line[j].x1))
                              *((line[j].x2-line[j].x1)*(line[i].y2-line[j].y1)-(line[j].y2-line[j].y1)*(line[i].x2-line[j].x1));
    				if(s1>0)
    				{
    					double s2=((line[j].x1-line[i].x1)*(line[i].y2-line[i].y1)-(line[j].y1-line[i].y1)*(line[i].x2-line[i].x1))
                              *((line[i].x2-line[i].x1)*(line[j].y2-line[i].y1)-(line[i].y2-line[i].y1)*(line[j].x2-line[i].x1));
    					if(s2>0)
    					{
    						ok=0;goto begin;
    					}
    				}
    			}
    		}
    begin:
    	    if(ok==1)
    			printf("ok!\n");
    		else
    			printf("burned!\n");
    
        }
        return 0;
    }


     

     

     

     



  • 相关阅读:
    mysql低版本升级到5.7
    mysql权限管理
    本地代码推送到远程git仓库
    解决ie低版本不认识html5标签
    使用ssh远程访问github
    centos7使用kubeadm搭建kubernetes集群
    js es6深入应用系列(Generator)
    js console一些常用的功能
    重新整理.net core 计1400篇[五] (.net core 修改为Startup模式 )
    重新整理.net core 计1400篇[五] (.net core 添加mvc 中间件 )
  • 原文地址:https://www.cnblogs.com/freezhan/p/2776494.html
Copyright © 2020-2023  润新知