数字旋转矩阵

                                       “螺旋数字方阵”解题报告

输入有多组数据,每组只有一行,包含x,y(1 <= x,y <= 30)和t,输出x*y螺旋方阵,如果t=0,就输出逆时针螺旋方阵,否则输出顺时针的

样例输入:

3 5 0

4 4 1

样例输出(每个数字要占四个格子,输出完一组就接着输出一个空行):

   1  12  11

   2  13  10

   3  14   9

   4  15   8

   5   6   7

 

   1   2   3   4

  12  13  14   5

  11  16  15   6

  10   9   8   7

通过记录:

方法一:

Name: "goal00001111" Problem ID "25"

Submit Time: 2010/4/28-14:59

G++: Compile OK

Test  1:    Accepted    Time = 1344 ms

--------------------------------

Problem ID     25

Test Result    Accepted

Total Time     1344 ms

Total Memory   172 Kb / 10000 Kb

Code Length    1797 Bytes

 

方法二:

Name: "goal00001111" Problem ID "25"

Submit Time: 2010/4/28-14:51

G++: Compile OK

Test  1:    Accepted    Time = 1400 ms

--------------------------------

Problem ID     25

Test Result    Accepted

Total Time     1400 ms

Total Memory   164 Kb / 10000 Kb

Code Length    1206 Bytes

 

方法三:

Name: "goal00001111" Problem ID "25"

Submit Time: 2010/4/29-07:51

G++: Compile OK

Test  1:    Accepted    Time = 1368 ms

--------------------------------

Problem ID     25

Test Result    Accepted

Total Time     1368 ms

Total Memory   164 Kb / 10000 Kb

Code Length    1203 Bytes

题目分析:

螺旋数字矩阵与蛇形矩阵一样,是练习巩固二维数组知识的经典题目。

基本思路是模拟生成自然数序列的过程,依次给二维数组的各个元素赋值,最后输出数组。

由于题目要求的输出可能有逆时针和顺时针两种方向,因此在模拟的时候要分两种情况。解决此问题有两种思路:一是分别为逆时针和顺时针编写代码;二是只编写一段代码,但多设置一个指示方向的变量dir,根据dir的值来决定旋转方向——此思路来自网友wind496,特向其表示感谢——其原理在于通过使行下标不变,列下标增1或减1,达到输入横向数据的目的;同理可以使列下标不变,行下标增1或减1,以输入纵向数据。

问题的另一个难点认清边界,避免出界和覆盖已写数据。我也采用了两种方法:一是设置四个边界变量:up,down,left,right。在程序执行过程中,确保(up <= down && left <= right)。若为逆时针旋转,则按照顺序排列纵排数字,顺序排列横排数字,逆序排列竖排数字,逆序排列横排数字的顺序生成数据。不断改变四个边界变量的值,直到跳出循环。

       另一种方法是多分配两行,列,设置“围墙”,以避免出界,判断map[i][j]是否等于0,以避免覆盖已写数据,当(row * col > s)时跳出循环。

两种算法比较:在处理旋转问题时,算法一思路直接,易于理解,但代码较长;算法二思路巧妙,但有些晦涩难懂——如果你能一次性领会其思路的话,说明你的理解能力高于常人——至少比我强,因为我最初阅读网友wind496的代码时,想了老半天才想通,而且,我已经对其代码进行了修改,更简洁,也更晦涩了,呵呵。

在处理边界问题时,算法一就不如算法二了,它是通过某点位置是否在边界范围内来决定是否输入数据;而算法二是通过判断该点是否为已处理点((0 == map[i] [j])表示该点未处理,否则已处理过)来决定是否输入数据。算法一有可能出现覆盖原有数据的错误(我是多次DEBUG后才解决该问题的),而算法二则不存在上述问题,而且通过设置“围墙”,巧妙地解决了越界的问题。

鉴于算法二给人们造成理解上的困难,我对其进行了改进,不是设置方向变量,而是设置二维方向数组dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,0},{0,-1},{1,0},{0,1}},dir[i][0]和dir[i][1]分别表示行下标和列下标的增减情况。其中i=[0..3]表示了顺时针旋转的情形,{0,1}表示行下标不变,列下标增1,实现顺序记录横向数据;{1,0}表示列下标不变,行下标增1,实现顺序记录纵向数据;{0,-1}表示行下标不变,列下标减1,实现逆序记录横向数据;{-1,0}表示列下标不变,行下标减1,实现逆序记录横向数据。多次重复上述过程,就可实现顺时针记录螺旋数字矩阵了。同理,i=[4..7]表示了逆时针旋转情形。

算法二的改进版增加了一个二维方向数组,使得代码量和理解难度都有所降低,也算是物有所值吧!

 

说明:

算法思想:模拟过程, 设置方向指针。

数据结构:二维数组。

时间复杂度:O(row * col);

空间复杂度:三种方法均为O(MAX*MAX)。

程序语言:c++。

附注:关于“螺旋数字方阵”的更多解法请参考《飞燕之家在线测评论坛OnlineJudge ? 新手习题区(OnlineJudge) ? 习题 25:螺旋数字方阵★:

http://yzfy.org/dis/listpost.php?tid=46&extra=page%3D1&page=1

 

c++代码:

 

#include <iostream>

#include <iomanip>

 

using namespace std;

 

const int MAX = 30;

 

void GetMatrix(int map[][MAX], int x, int y, int t);

 

int main()

{

      int matrix[MAX][MAX] = {0};

      int x, y, t;

   

    while (cin >> x >> y >> t)

      {

           GetMatrix(matrix, y, x, t);

      }

     

      return 0;

}

 

/*

函数名称:GetMatrix

函数功能:输出row*col螺旋矩阵

输入变量:int map[][MAX]:存储了螺旋矩阵的二维数组

          int row, col: 螺旋矩阵的行数和列数

          int t:决定输出螺旋矩阵的方向,如果t=0,逆时针输出,否则顺时针输出

输出变量:int map[][MAX]:存储了螺旋矩阵的二维数组

返回值:无

*/

方法一:分别为逆时针和顺时针编写代码。

void GetMatrix(int map[][MAX], int row, int col, int t)

{

    int up =0, down = row - 1, left = 0, right = col - 1;//设定四个边界

    int num = 0;

   

    if (0 == t)

    {

        while (up <= down && left <= right)

        {

            for (int i=up; i<=down; i++)//顺序排列纵排数字

                map[i][left] = ++num;

           

            if (left == right) //避免重复输出列

                break;

                 for (int i=++left; i<=right; i++)//顺序排列横排数字

                map[down][i] = ++num;

                

                 if (up == down) //避免重复输出行

                break;   

                 for (int i=--down; i>=up; i--)//反序排列竖排数字

                map[i][right] = ++num;

                                     

            for (int i=--right; i>=left; i--)//反序排列横排数字

                map[up][i] = ++num; 

           

                 up++;

        }  

    }

    else

    {

        while (up <= down && left <= right)

        {

            for (int i=left; i<=right; i++)//顺序排列横排数字

                map[up][i] = ++num;

           

            if (up == down) //避免重复输出行

                break; 

            for (int i=++up; i<=down; i++)//顺序排列竖排数字

                map[i][right] = ++num;

           

            if (left == right) //避免重复输出列

                break; 

            for (int i=--right; i>=left; i--)//反序排列横排数字

                map[down][i] = ++num;

                       

            for (int i=--down; i>=up; i--)//反序排列竖排数字

                map[i][left] = ++num;

               

            left++;

        }  

    }

   

    for (int i=0; i<row; i++) //输出矩阵

    {

           for (int j=0; j<col; j++)

                 cout << setw(4) << map[i][j];

           cout << endl;

      }

      cout << endl;

}

 

方法二:设置一个指示方向的变量dir,根据dir的值来决定旋转方向。注意要多分配两行,列,设置“围墙”,以避免出界。

void GetMatrix(int map[][MAX], int row, int col, int t)

{   //数据初始化

    for (int i=0; i<=row+1; i++)

          for (int j=0; j<=col+1; j++)

          {

                if (0 == i || 0 == j || row < i || col < j)

                map[i][j] = 1;

            else

                map[i][j] = 0;

        }

       

    int dir = (0 == t) ? -1 : 1; //指示方向:1表示顺时针,-1表示针逆时针

      int addrow = 0, addcol = dir;   //分别表示行下标和列下标的前进方向:前进,后退或不动

      int s, i, j;

     

      s = i = j = map[1][1] = 1;

      while (row * col > s)

      {

        while (0 == map[i+addrow][j+addcol]) //可输入数据

                 map[i+=addrow][j+=addcol] = ++s; //改变行,列下标的值,同时记录该点数据

       

        //此处考验你的阅读理解能力,呵呵!

          if (0 == addrow) //若刚刚输入的是横向数据,转为纵向输入

          {

            addrow = (1 == addcol) ? dir : -dir;//若为横向递增,则顺着旋转方向走(addrow=dir);否则逆着走

            addcol = 0;

        }

        else  //若刚刚输入的是纵向数据,转为横向输入

        {

                addcol = (1 == addrow) ? -dir : dir;//若为纵向递增,则逆着旋转方向走(addrow=-dir);否则顺着走

            addrow = 0;

           }

    }

   

    for (int i=1; i<=row; i++) //输出矩阵

    {

           for (int j=1; j<=col; j++)

                 cout << setw(4) << map[i][j];

           cout << endl;

      }

      cout << endl;

}

 

方法三:算法二的改进版,不是设置方向变量,而是设置二维方向数组。

void GetMatrix(int map[][MAX], int row, int col, int t)

{   //数据初始化

    for (int i=0; i<=row+1; i++)

          for (int j=0; j<=col+1; j++)

          {

                if (0 == i || 0 == j || row < i || col < j)

                map[i][j] = 1;

            else

                map[i][j] = 0;

        }

       

    int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,0},{0,-1},{1,0},{0,1}};//指示旋转方向游标

      int begin, end; //根据选择逆时针还是顺时针,确定4个旋转方向

      int s, r, c;

     

      if (0 == t)//逆时针

      {

        begin = 4;

        end = 8;

    }

    else//顺时针

    {

        begin = 0;

        end = 4;

    }

   

      s = r = c = map[1][1] = 1;

      while (row * col > s)

      {

            for (int i=begin; i<end; i++) //每轮循环输入一条边,不断旋转,直到全部记录全部数字

           {

              while (0 == map[r+dir[i][0]][c+dir[i][1]]) //可输入数据

                      map[r+=dir[i][0]][c+=dir[i][1]] = ++s; //改变行,列下标的值,同时记录该点数据

          }

    }

   

    for (int i=1; i<=row; i++) //输出矩阵

    {

           for (int j=1; j<=col; j++)

                 cout << setw(4) << map[i][j];

           cout << endl;

      }

      cout << endl;

}

posted @ 2015-03-23 15:32  Spiderman.L  阅读(1751)  评论(0编辑  收藏  举报