C语言 螺旋方阵的生成方法分析

来源:互联网 发布:软件开发 项目 编辑:程序博客网 时间:2024/05/04 16:18

题目:

Input Height N, printf such pattern: 

   1   2   3   4   5 
  16  17  18  19   6 
  15  24  25  20   7 
  14  23  22  21   8 
  13  12  11  10   9 

第一种解法:是用等差数列解题。 
算法: 
   1   2   3   4   5 
  16  17  18  19   6 
  15  24  25  20   7 
  14  23  22  21   8 

  13  12  11  10   9 


定义行i列j。从外向内分层,一圈一层。定义r:(r>=1)当前圈为第几圈。 
定义N:图形高度 
定义l:当前圈边长l 


l(1)=N-1 

l(r)=N-2*(r-1)-1 
设当前圈左上角为开始。定义d:当前行的方向(上边为0右边为1下边为2左边为3) 
定义pl:当前圈当前边距角的长度(向数字小的方向) 
定义a:当前圈共有多少个数字 
a(r)=4*l(r)=4*(N-2*(r-1))-4 
公差为-8 
从最外圈到当前圈求和:s(r)=a(1)*r-r*(r-1)/2*(-8) 
定义b:本圈第一个数字 
b(r)=s(r-1)+1 
    =a(1)*(r-1)-(r-1)*(r-2)/2*(-8)+1 
开始计算: 


求r:  r=min(i,N-j+1,N-i+1,j) 

求d:  d为(i,N-j+1,N-i+1,j)与r对应,如r如第1个值,则d为第一个值... 

求pl:   p=(j-r,i-r,N-j+1-r,N-i+1-r)与r对应,如r如第1个值,则d为第一个值... 


当前数值b+l*d+pl 

Commented by Wang Rui 
 程序如下: 
main( ) 
{ int i,j,r,l,b,d,pl,N; 
printf("Input Height:"); /*输入高度*/ 
scanf("%d",&N); 
for (i=1;i<=N;i++) 
{ for (j=1;j<=N;j++) /*定位到第i行第j列*/ 
{ r=i;d=0;pl=j-r; 
if (r>N-j+1) {r=N-j+1;d=1;pl=i-r;} 
if (r>N-i+1) {r=N-i+1;d=2;pl=N-j+1-r;} 
if (r>j) {r=j;d=3;pl=N-i+1-r;} 
/*找出圈数,边的位置,在边上的位置*/ 
l=N-2*(r-1)-1;/*边长*/ 
b=(4*N-4)*(r-1)-4*(r-1)*(r-2)+1; 
/*本圈开始的数字*/ 
printf("%4d",b+l*d+pl);/*打印数字*/ 

printf("\n"); 



第二种解法:这是用几何方法解的。 

main( ) 
{ int i,j,N,c; 
printf("Input N="); 
scanf("%d",&N); 
for (i=1;i<=N;i++) 
for (j=1;j<=N;j++) 
{ if (j>=i&&j<=N+1-i||j<=i&&j>=N+1-i) 
if (i<=(N+1)/2) 
c=(i-1)*(4*N-4*i+3)+j; 
else 
c=2*(N-i)*(2*i-1)+3*i-j-1; 
else 
if (j<=(N+1)/2) 
c=4*N*j-4*j*j-i+j+1; 
else 
c=(N-j)*(4*j-3)+N+i-1; 
printf((j==N)?"%4d\n":"%4d",c); 



第三种解法:用面积解。 
main( ) 
{ int i,j,r,l,b,d,pl,N; 
printf("Input Height:"); /*输入高度*/ 
scanf("%d",&N); 
for (i=1;i<=N;i++) 
{ for (j=1;j<=N;j++) /*定位到第i行第j列*/ 
{ r=i;d=0;pl=j-r; 
if (r>N-j+1) {r=N-j+1;d=1;pl=i-r;} 
if (r>N-i+1) {r=N-i+1;d=2;pl=N-j+1-r;} 
if (r>j) {r=j;d=3;pl=N-i+1-r;} 
/*找出圈数,边的位置,在边上的位置*/ 
l=N-2*(r-1);/*边长*/ 
b=N*N-l*l+1;/*用面积计算*/ 
/*本圈开始的数字*/ 
printf("%4d",b+(l-1)*d+pl);/*打印数字*/ 

printf("\n"); 



  第4种解法。  

分析:首先寻找数字输出数字和行列的关系。  


    每圈有四个边,把每边的最后一个数字算为下边的开始,最外圈每边数字个数是n-1个,以后每边比外边一边少两个数字。  
 
    因为数字是一行一行输出的,再分析每行数字的规律。实际没有的数字有三种规律:位于对角线之间的数字是上半图增一,下半图减一。对角线左侧的各列,右侧比左侧增加了一圈数字,例如数字39和它左侧的22比较,数字39所在的圈每边4个数字,左侧22加上一圈16个数字在加1就是39。同理,对角线右侧的各列,则减少一圈的数字个数。  


    根据以上分析,用两个对角线将图形分为四个区域,如下图所示,图中黑斜体字为对角线上的数字。  


                        1   2   3   4   5   6   7 


                       24  25  26  27  28  29   8  


                       23  40  41  42  43  30   9  


                       22  39  48  49  44  31  10  


                       21  38  47  46  45  32  11  


                       20  37  36  35  34  33  12  


                       19  18  17  16  15  14  13  


    为叙述方便我们称四个区域为上、下、左、右区。设i、j为行列号,n为图形的总行数,则满足各区的范围是:  


    上区:j>=i且j<=n-i+1    下区:j<=i且j>=n-i+1  


    左区:j<i且j<n-i+1    右区:j>i且j>n-i+1  


    现在问题是,如果知道一行在不同区域开始第一个位置的数字,然后该区后续的数字就可利用前面分析的规律得到。对于右区开始各行第一个数字最易求出,为4*(n-1)-i+1。后续一个和同行前一个数字之差是4*[n-1-(j-1)*2]+1,其中方括号内是每边的数字个数。对角线上的数字是分区点,对角线上相临数字仍然相差一圈数字个数,读者自行分析得到计算公式。右区开始的第一个数字可以从上区结束时的数字按规律求出。下述程序用变量s保存分区对角线上的数字。 


参考答案:  


main( )  


{    int i,j,k,n,s,m,t;  


     printf("Please enter n:");  


     scanf("%d",&n);  


     for (i=1;i<=n;i++)  


     {     s = (i<=(n+1)/2)? 1:3*(n-(n-i)*2-1)+1;  


           m = (i<=(n+1)/2)? i:n-i+1;                 /* m-1是外层圈数 */  


           for (k=1;k<m;k++)  s+=4*(n-2*k+1);  


           for (j=1;j<=n;j++)  


           {   if (j>=n-i+1 &&  j<=i)                  /* 下区 */  


                  t=s-(j-(n-i))+1;  


               if (j>=i && j<=n-i+1)                   /* 上区 */  


                  t=s+j-i;  


               if (j>i && j>n-i+1)                     /* 右区 */  


                  t -= 4*(n-2*(n-j+1))+1;  


               if (j<i && j<n-i+1)                     /* 左区 */  


               {   if (j==1)  t=4*(n-1)-i+2;  


                   else       t+=4*(n-2*j+1)+1;  


               }  


               printf("%4d",t);  


           }  


           printf("\n");  


     }  


}  


   


第5种解法:递归方法。  


分析:根据本题图形的特点,我们可以构造一个递归算法。我们可以将边长为N的图形 分为两部分:第一部分最外层的框架,第二部分为中间的边长为N-2的图形。对于边长为N的正方型,若其中每个元素的行号为i(1≤i≤N),列号为j(1≤j≤N),第1行第1列元素表示为a1,1(a11=1),则有:对于最外层的框架可以用以下数学模型描述: 


         上边:  a1,j=a1,1+j-1          (j≠1)  


         右边:  ai,N=a1,1+N+i-2        (i≠1)  


         下边:  ai,1=a1,1+4N-i-3       (i≠1)  


         左边:  aN,j=a1,1+3N-2-j       (j≠1)  


    对于内层的边长为N-2的图形可以用以下数学模型描述:  


         左上角元素:ai,i=ai-1,i-1+4(N-2i-1)   (i>1)  


若令:ai,j=fun(ai-1,i-1+4(N-2i-1),当:i<(N+1)/2且j<(N+1)/2时,min=MIN(i,j),则有:  


     a2,2 = fun(a1,1, min, min, n)  


     ai,j=fun(a2,2, i-min+1, j-min+1, n-2*(min-1) )  


我们可以根据上述原理,分别推导出i和j为其它取值范围时的min取值。根据上述递归公式,可以得到以下参考程序。  


#include <stdio.h>  


#define MIN(x,y)  (x>y) ? (y) : (x)  


fun ( int a11, int i, int j, int n)  


{ int min, a22;  


  if ( i==j && i<=1 ) return a11;  


  else if ( i==j && i<=(n+1)/2) return ( fun(a11,i-1,i-1,n)+4*(n-2*i+3) );  


  else if ( i==1 && j!=1 ) return ( a11+j-1 );  


  else if ( i!=1 && j==n ) return ( a11+n+i-2 );  


  else if ( i!=1 && j==1 ) return ( a11+4*n-3-i );  


  else if ( i==n && j!=1 ) return ( a11+3*n-2-j );  


  else  


  {  if (i>=(n+1)/2 && j>=(n+1)/2)     min = MIN(n-i+1,n-j+1);  


     else if (i<(n+1)/2 && j>=(n+1)/2) min = MIN(i,n-j+1);  


     else if (i>=(n+1)/2 && j<(n+1)/2) min = MIN(n-i+1,j);  


     else                              min = MIN(i,j);  


     a22 = fun(a11,min,min,n);  


     return  fun(a22, i-min+1, j-min+1, n-2*(min-1) );  


  }  


}  


main ( )  


{ int a11=1, i, j, n;  


  printf ("Enter n=");  


  scanf("%d", &n);  


  for (i=1; i<=n; i++)  


  {  for (j=1; j<=n; j++)  


        printf ("%4d", fun(a11,i,j,n) );  


     printf ("\n");  


  }  


}  













0 0