首页 > 编程语言 > 详细

螺旋矩陣 非数组解法

时间:2016-03-06 11:15:00      阅读:210      评论:0      收藏:0      [点我收藏+]

本題要求將給定的n*n個正整數按遞增的順序,填入“螺旋矩陣”。所謂“螺旋矩陣”,是指從左上角第1個格子開始,按順時針螺旋方向填充數字,數字從1開始直到n*n。要求矩陣的規模為n行n列。【不允許使用數組】

輸入格式:

輸入在第1行中給出一個正整數n。

輸出格式:

輸出螺旋矩陣。每行n個數字,共n行。相鄰數字以空格分隔。

輸入樣例:
4
輸出樣例:
1   2   3   4
12 13 14  5
11 16 15  6
10 9   8   7 

============================ 

解法一 By 子木

解決步驟:

1. 簡單情況處理(n是否小於等於2)

2. 矩陣劃分

  2.1 整體劃分

  2.2. 遞歸劃分

3. 解決外圍部分

4. 解決裡邊部分

 

1. 簡單情況處理(n是否小於等於2)

     首先判斷n是否小於等於2。n小於等於2的螺旋矩陣,實際上就是一個簡單的順時針矩陣。另行解決即可。

     示例如下:

  1            1  2 
        4  3

 

2. 矩陣劃分

     這一節希望將矩陣劃分出兩個部分,分別是容易解決的外圍部分,和可以遞歸解決的裡邊部分。

  如果n>2,那麼可以將螺旋矩陣分成兩個部分,分別是最外面一圈“空心正方形框”和裡邊一坨“實心正方形” 。假定,只有一個數字的也算邊長為1的正方形。

  示例如下:

  1  1  1               1  1  1  1          1  1  1  1  1
  1  2  1               1  2  2  1          1  2  2  2  1
  1  1  1                  1  2  2  1          1  2  2  2  1        
Fig.1 n=3, 分成兩個部分         1  1  1  1             1  2  2  2  1          
                Fig.2 n=4, 分成兩個部分       1  1  1  1  1 

                             Fig.3 n=5, 分成兩個部分

  這裡有疑問:裡邊那個一定是正方形嗎?為什麼n需要大於2?

      裡邊的實心正方形部分,是在原有的正方形基礎上取出外圍,實際上是將排和列都減去2。

      由於原本是正方形,所以排與列都減去2后,只要邊長(n)大於0,一定仍然為正方形。 

      因此,邊長(n)大於2是為了讓原本正方形能分成兩個部分,並且保證了裡邊的部分仍為正方形。

  下面我們介紹怎樣將一個矩陣分為外圍部分和裡邊部分。

 

   2.1 整體劃分

  當我們想要打印第i行第j列的數字時,怎麼知道這個數字是屬於外圍部分(空心正方形框)還是裡邊部分(實心正方形)呢?

  先定義一個操作“鏡像反轉”:

    鏡像反轉,指的是對一個數字序列1, 2, 3, ..., a-1, a,將其變成1, 2, 3, ..., 2, 1

    舉個例子,12 34 變成 12 21;12 3 45 變成 12 3 21;123 456 變成 123 321.

    對每個數字i, 通項公式是t=a-i+1

  接著定義一個操作"12鏡像反轉":

    12鏡像反轉,指的是將鏡像反轉之後的序列中,大於2的數字全部變成2.

    舉個例子,12 34 變成 12 21;12 3 45 變成 12 2 21;123 456 變成 122 221.

    對每個數字i, 通項公式是t=min{a-i+1, 2}

  如果我們將i“12鏡像反轉”成k ,將j“12鏡像反轉”成t。通過觀察,我們發現,取k和t中比較小的一個,如果是1,這個數字屬於外圍部分;如果是2,這個數字屬於裡邊部分。

对i(行):      1  1  1  1      镜像后k:1  1  1  1  
                         2  2  2  2                      2  2  2  2
                         3  3  3  3                      2  2  2  2
                         4  4  4  4                      1  1  1  1
                            
对j(列):      1  2  3  4      镜像后t:1  2  2  1
                         1  2  3  4                     1  2  2  1
                         1  2  3  4                     1  2  2  1
                         1  2  3  4                     1  2  2  1  

k和t中較小一個:

                         1  1  1  1
                         1  2  2  1
                         1  2  2  1
                         1  1  1  1 

 

 2.2. 遞歸劃分

  如果n>4,我們發覺裡邊一坨“實心正方形”又可以繼續拆成外面一圈“空心正方形框”和裡邊一坨“實心正方形”。

  直到變成步驟2中Fig.1或Fig.2為止。

    1  1  1  1  1    
    1  2  2  2  1    2  2  2            1  1  1
    1  2  2  2  1 =>      2  2  2   =>     1  2  1         
    1  2  2  2  1      2  2  2     1  1  1           
    1  1  1  1  1 

         Fig.4 n=5, 不斷分兩個部分

  實際上這個問題變成解決外圍部分和裡邊部分的問題。而裡邊部分是可以又分成解決其外圍部分和裡邊部分的子問題。

  裡邊部分不斷剝離外圍,最終會變成Fig.1或Fig.2的簡單問題。

  因此我們下面要做的是:1)解決外圍部分;2)【子問題】解決裡邊部分>>>[本質]解決Fig.1或Fig.2的裡邊部分。

3. 解決外圍部分

  實際上是一個順時針矩陣,另行解決即可。

4. 解決裡邊部分

  本質是解決Fig.1或Fig.2的裡邊部分。觀察可以發現,實際上跟步驟1完全相同,照樣解決即可。

 

C++ code

#include<iostream>
#include<cmath>
using namespace std;
int around(int ,int,int);
int main()
{
 int n,m,an,a,all,i,j,t,k,center,unit,extra;//an为所在正方形边长,all为正方形总个数
 cin>>m;
 a=m ;//求出a为最大正方形(n=1)边长
 if(a%2==0) all=a/2;//奇数和偶数会使正方形个数发生变化
 else all=a/2+1;
 for(i=1;i<=a;i++)
 { 
  if (i<=a/2)
    t=i;//若i超过中间线,使t为i镜像对称 else
    t=a-i+1; for(j=1;j<=a;j++) {   if (j<=a/2)
      k=j;//同理,使k为j镜像对称 else
      k=a-j+1; if(k<=t)
      n=k;//k和j间较小一个决定了所在正方形n else
      n=t; if(a%2==0) //奇偶数不一样 {
       an
=2*(all-n+1);//第n个正方形边长 center=a/2;//求中间线 } else {
an
=2*(all-n+1)-1; center=a/2+1;
}
if(i==n) //在顶边    extra=j-n+1; else if(i<(n-1)+an) //在两边 {   if(j<=center) //在左边   extra=(n-1)+an-i+3*an-2; else //在右边   extra=an+(i-(n-1)-1); } else //在底边   extra=(2*an-1)+(an- ( j-(n-1) ) ); unit=around(n-1,a,all)+extra;//数值为外圈數字個數加额外数 cout<<unit<<"\t";
    }   cout
<<endl;
  }
return 0; } int around(int n,int a,int all)//递归不斷分解出外围數字個數 {
int an; if(a%2==0) an=2*(all-n+1); //求边长   else  an=2*(all-n+1)-1; if(n==0)
    return 0; else
   return around(n-1,a,all)+4*an-4;//由边长求周长 }

 

=====================================

解法2 by燦杰

经过一晚思考,终于出炉啦,一样感觉,矩阵还是数组好做呀。尽量解释哈,看的懂就看吧,其实挺好理解的。
 
想法:
用第i行,第j列,i,j作为变量,用一道公式算出坐标为(i,j)的数。
 
做法:
先将矩阵通过对称折叠成左上角的四分之一大小矩阵,
在同一个正方形圈的数与在四分之一矩阵内的圈内的数坐标重叠,折叠后的数的坐标用(ii,jj)表示;
可找到,ii,jj中小的那个数就是该数所在的圈,然后可由n和圈数算出小于该圈所有数的最大的整数,这里我称之为基数;
然后将该数所在圈移动至左上角,方便计算该数在圈内的位置,得到新坐标(i1,j1)
由新坐标的i1,j1参与判断,选用公式计算该数在圈内所在位置,其实形象上看是一个将圈拉成直线的过程。
然后基数+位数,得出该数大小。
 
#include<iostream>
using namespace std;
int main()
{
  int n,i,j,ii,jj,k,i1,j1;
  cout<<"请输入n:"<<endl;
  cin>>n;
  for(i=1;i<=n;i++)//i表示第i行,j表示第j列,逐行逐个数字输出
  {
    for(j=1;j<=n;j++)
    {
      if(i>n/2) //将矩阵折叠成四分之一
        ii=n-i+1;//ii,jj分别表示在四分之一矩阵中的第几行,第几列
      else
        ii=i;
      if(j>n/2)
        jj=n-j+1;
      else
        jj=j;
      if(ii>jj)//求出从外内,该数在第几个圈,并在此用ii表示第几圈
        ii=jj;
        i1=i-ii+1;//将该数所在的圈移动至左上角,然后用i1,j1表示该数的新坐标
        j1=j-ii+1;
      if(i>j)//将圈拉直,计算该数为该圈的第几个数
        k=4*n-8*ii+7-i1-j1;
      else
        k=i1+j1-1;
        cout<<n*n-(n-2*ii+2)*(n-2*ii+2)+k<<\t;//该圈的基数加上该数在该圈的位置,输出该数
    }
    cout<<endl;//换行
  }
}

 

=====================================

解法3 by朱鵬

#include <iostream>
using namespace std;
void show(int,int,int,int,int);
int main()
{
 int n,i,a,j=0;
 int b=0;
 cout<<"输入你想构建的N:"<<endl;
 cin>>n;
 for(i=1;i<=n;i++)
 {
  if(i<=n/2)
  {
   show(i,i,n,b,j);
  }
  else
  {
   a=n-i+1;
   show(i,a,n,b,j);
  }
   cout<<endl;
 }
}
void show(int k,int g,int n,int b,int j)
{
 int i,d=n;
 if(j>0)
 {
   k--;g--;
  
  b+=4*n-4;
  n=n-2;
 }
 if(g>1)
 {
  cout<<"   "<<b+(4*n-4-k+2);
  j++;
  show(k,g,n,b,j);
  cout<<"   "<<b+n+k-1;
 }
 else 
 {
  if(k!=n)
  {
   for(i=b+1;i<=b+n;i++)
    cout<<"   "<<i;
  }
  else
  {
   int c=b+(4*n-4-n+2),l=b+2*n-1;
   for(i=c;i>=l;i--)
    cout<<"   "<<i;
  }
 }

 

=====================================

解法四 by禮權

禮權的代碼沒有發出來……

如果沒記錯的話,大概是把正方形分成3部分來解

1  1  1  1  1

2  1  1  1  3

2  2  1  3  3

2  1  1  1  3

1  1  1  1  1

 

=====================================

TA课上提及,对自己以前的博文整理,提高可读性。原文鏈接:

http://user.qzone.qq.com/312677150/blog/1289724415

螺旋矩陣 非数组解法

原文:http://www.cnblogs.com/zeedmood/p/5229463.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!