• 数字旋转矩阵


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

    输入有多组数据,每组只有一行,包含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;

    }

  • 相关阅读:
    exFAT移动硬盘写保护怎么去掉
    ORACLE:一列的多行数据拼成字符串
    cxgrid中,如何根据列名或字段名取得footer值
    Delphi天气预报查询
    Delphi ListView基本用法大全
    datasnap的初步
    Delphi ListView基本用法大全
    Delphi实现树型结构
    获取身份证号码信息
    让Delphi XE5跟其他版本的Delphi共存
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/4359952.html
Copyright © 2020-2023  润新知