题解:学而思编程 回形矩阵

【题目来源】

学而思编程:回形矩阵

【题目描述】

给定一个 \(n\)\(m\) 列的二维数组,要求从二维数组的左上角位置开始,沿顺时针或逆时针方向将数字 \(1\)\(n * m\) 按照回形从外向内依次填入数组中。

【输入】

第一行两个整数 \(n\)\(m\)

第二行一个字符 \(d\),其中'1'表示顺时针方向,'0'表示逆时针方向。

【输出】

输出一个 \(n\times m\) 的二维数组,表示从二维数组的左上角位置开始,沿 \(d\) 方向将数字 \(1\)\(n * m\) 填入数组之后的结果。

【输入样例】

3 3
1

【输出样例】

1 2 3
8 9 4
7 6 5

【核心思想】

  1. 问题分析:给定 \(n\)\(m\) 列的矩阵,从左上角开始,按顺时针或逆时针方向以回形(螺旋)方式填入 \(1\)\(n \times m\)。这是一个方向模拟问题,关键在于维护当前位置和方向,在边界或已填位置处自动转向。

  2. 算法选择

    • 方向数组:用 dx[4]dy[4] 定义四个基本方向(右、下、左、上)
    • 碰壁转向:每次尝试沿当前方向前进,若越界或目标位置已填,则按顺时针或逆时针规则转向
    • 转向参数:用 p = 1(顺时针)或 p = -1(逆时针)控制方向索引的变化
  3. 关键步骤

    • 初始化
      • 读取 \(n\)(行数)、\(m\)(列数)、\(d\)(方向标志,1 顺时针,0 逆时针)
      • 矩阵 g[1..n][1..m] 全局定义,自动初始化为 \(0\)
      • 设置起始位置 \((x, y) = (1, 1)\)
      • \(d = 1\)dir = 0(初始向右),p = 1(顺时针转向:右→下→左→上)
      • \(d = 0\)dir = 1(初始向下),`p = -1$(逆时针转向:下→右→上→左)
    • 螺旋填数(遍历 \(i\)\(1\)\(n \times m\)):
      • g[x][y] = i:在当前位置填入数字
      • 计算下一步坐标:\(a = x + dx[dir]\)\(b = y + dy[dir]\)
      • 若越界(\(a < 1\)\(a > n\)\(b < 1\)\(b > m\))或已填(g[a][b] \neq 0):
        • dir = (dir + p + 4) \bmod 4:按规则转向(加 \(4\) 保证非负)
        • 重新计算 \(a, b\)
      • x = a, y = b:移动到新位置
    • 输出矩阵:按行输出 g[1..n][1..m]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times m)\),每个位置恰好填入一次
    • 空间复杂度:\(O(n \times m)\),存储矩阵
  5. 方向模拟的核心思想

    • 方向编码:四个方向用索引 \(0, 1, 2, 3\) 编码,方向数组实现坐标变化的统一处理,避免大量 if-else
    • 碰壁检测:不预计算边界,而是"走一步看一步",通过越界检查和已填标记自动触发转向,代码简洁且鲁棒
    • 转向统一化:顺时针 dir = (dir + 1) \bmod 4,逆时针 dir = (dir - 1 + 4) \bmod 4,用参数 p 统一为 (dir + p + 4) \bmod 4
    • 初始方向差异化:顺时针从向右开始(符合阅读习惯),逆时针从向下开始,通过 dir 初值区分
    • 适用于"矩阵螺旋遍历/填充"类问题,方向数组 + 碰壁转向是经典模板,可扩展至多层螺旋、蛇形填充等变种

【算法标签】

模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int dx[4] = {0, 1, 0, -1};  // 方向数组:右、下、左、上
int dy[4] = {1, 0, -1, 0};
/*
顺时针:右->下->左->上->右...
逆时针:下->右->上->左->下...
*/

int g[N][N];  // 存储矩阵
int n, m, d;  // 行数、列数、方向标志

int main()
{
    cin >> n >> m >> d;  // 输入n行m列,方向d

    int x = 1, y = 1, dir, p;  // 当前位置(1,1),方向,转向参数

    // 根据d初始化方向
    if (d == 1)  // 顺时针
    {
        dir = 0;  // 初始向右
        p = 1;    // 顺时针旋转:+1
    }
    else  // 逆时针
    {
        dir = 1;  // 初始向下
        p = -1;   // 逆时针旋转:-1
    }

    // 填数1到n*m
    for (int i = 1; i <= n * m; i++)
    {
        g[x][y] = i;  // 填写数字

        // 尝试沿着当前方向前进
        int a = x + dx[dir], b = y + dy[dir];

        // 如果越界或已经填过,需要转向
        if (a < 1 || a > n || b < 1 || b > m || g[a][b] != 0)
        {
            // 转向:(dir + p + 4) % 4
            dir = (dir + p + 4) % 4;
            a = x + dx[dir], b = y + dy[dir];
        }

        x = a, y = b;  // 移动到下一个位置
    }

    // 输出矩阵
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << g[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

【运行结果】

3 3
1
1 2 3
8 9 4
7 6 5
posted @ 2026-07-04 14:31  团爸讲算法  阅读(11)  评论(0)    收藏  举报