zoukankan      html  css  js  c++  java
  • zoj 1081 (改进的弧长算法)(转)

      看到网上除了射线法,很长一段代码之外,看到了一个很简单的算法解决这个问题,特意转了过来

    /*
     这个算法是源自《计算机图形学基础教程》(孙家广,清华大学出版社),在该书 
    的48-49页,名字可称为"改进的弧长法"。该算法只需O(1)的附加空间,时间复杂度为O 
    (n),但系数很小;最大的优点是具有很高的精度,只需做乘法和减法,若针对整数坐标则 
    完全没有精度问题。而且实现起来也非常简单,比转角法和射线法都要好写且不易出错。 
    
    
          首先从该收中摘抄一段弧长法的介绍:"弧长法要求多边形是有向多边形,一般规 
    定沿多边形的正向,边的左侧为多边形的内侧域。以被测点为圆心作单位圆,将全部有向 
    边向单位圆作径向投影,并计算其中单位圆上弧长的代数和。若代数和为0,则点在多边形 
    外部;若代数和为2π则点在多边形内部;若代数和为π,则点在多边形上。" 
    
          按书上的这个介绍,其实弧长法就是转角法。但它的改进方法比较厉害:将坐标原 
    点平移到被测点P,这个新坐标系将平面划分为4个象限,对每个多边形顶点P,只考虑 
    其所在的象限,然后按邻接顺序访问多边形的各个顶点P,分析P和P[i+1],有下列 
    三种情况: 
        (1)P[i+1]在P的下一象限。此时弧长和加π/2; 
        (2)P[i+1]在P的上一象限。此时弧长和减π/2; 
        (3)P[i+1]在Pi的相对象限。首先计算f=y[i+1]*x-x[i+1]*y(叉积),若f= 
    0,则点在多边形上;若f<0,弧长和减π;若f>0,弧长和加π。 
    
          最后对算出的代数和和上述的情况一样判断即可。 
    
          实现的时候还有两点要注意,第一个是若P的某个坐标为0时,一律当正号处理; 
    第二点是若被测点和多边形的顶点重合时要特殊处理。     
    
          以上就是书上讲解的内容,其实还存在一个问题。那就是当多边形的某条边在坐标 
    轴上而且两个顶点分别在原点的两侧时会出错。如边(3,0)-(-3,0),按以上的处理,象限 
    分别是第一和第二,这样会使代数和加π/2,有可能导致最后结果是被测点在多边形外。 
    而实际上被测点是在多边形上(该边穿过该点)。 
          对于这点,我的处理办法是:每次算P和P[i+1]时,就计算叉积和点积,判断该 
    点是否在该边上,是则判断结束,否则继续上述过程。这样牺牲了时间,但保证了正确性 
    。 
          具体实现的时候,由于只需知道当前点和上一点的象限位置,所以附加空间只需O( 
    1)。实现的时候可以把上述的"π/2"改成1,"π"改成2,这样便可以完全使用整数进 
    行计算。不必考虑顶点的顺序,逆时针和顺时针都可以处理,只是最后的代数和符号不同 
    而已。整个算法编写起来非常容易。 
    
    */
    #include <stdio.h> 
    #include <math.h> 
    const int MAX = 101 ; 
    struct point 
    { 
        int x , y ;
    } p[MAX] ; 
    int main() 
    { 
        int n , m , i , sum , t1 , t2 , f , prob = 0 ; 
        point t ; 
        while (scanf("%d",&n),n) 
        { 
            if(prob ++) 
                printf ("
    "); 
              printf ("Problem %d:
    ",prob) ; 
            scanf ("%d" ,&m) ; 
            for (i = 0; i < n;i++) 
                scanf ("%d%d",&p[i].x,&p[i].y) ; 
            p[n] = p[0] ; 
            while(m--) 
            { 
                    scanf ("%d%d",&t.x,&t.y); 
                    for (i=0;i<=n;i++)
                        p[i].x -=t.x,p[i].y -= t.y ;                                        // 坐标平移,将被测点设置为坐标原点  
                    t1 = p[0].x>=0 ? ( p[0].y>=0?0:3 ) : ( p[0].y>=0?1:2 ) ;                 // 计算象限 
                    for (sum = 0,i =1;i <= n;i ++ ) 
                    { 
                        if ( !p[i].x && !p[i].y ) break ;                                     // 被测点为多边形顶点 
                           f = p[i].y * p[i-1].x - p[i].x * p[i-1].y ;                         // 计算叉积 
                        if ( !f && p[i-1].x*p[i].x <= 0 && p[i-1].y*p[i].y <= 0 ) break ;   // 点在边上 
                        t2 = p[i].x>=0  ? ( p[i].y>=0 ?0:3 ) : ( p[i].y>=0?1:2 ) ;             // 计算象限 
                        if ( t2 == ( t1 + 1 ) % 4 )      sum += 1 ;                          // 情况1 
                        else if ( t2 == ( t1 + 3 ) % 4 ) sum -= 1 ;                          // 情况2 
                        else if ( t2 == ( t1 + 2 ) % 4 )                                     // 情况3 
                              if ( f > 0 )                sum += 2 ; 
                              else                          sum -= 2 ;
                        t1 = t2 ; 
                    } 
                    if ( i<=n || sum ) printf( "Within
    " ) ; 
                    else               printf( "Outside
    " ) ; 
                    for ( i = 0 ; i <= n ; i ++ ) 
                        p[i].x += t.x , p[i].y += t.y ;             // 恢复坐标 
            } 
        } 
            return 0; 
    }
  • 相关阅读:
    iOS socket编程 第三方库 AsyncSocket(GCDAsyncSocket)
    JS中reduce方法
    程序员的运动建议
    Vuex(三)—— getters,mapGetters,...mapGetters详解
    微信小程序之使用函数防抖与函数节流
    JS 异步(callback→Promise→async/await)
    圈子与网络
    社会经验4
    社会经验3
    爱情(。_。)大忌
  • 原文地址:https://www.cnblogs.com/woshijishu3/p/3657312.html
Copyright © 2011-2022 走看看