#LC1260. 二维网格迁移

二维网格迁移

你好,同学。今天我们来研究一个关于二维矩阵变换周期性规律的经典题目。这道题在 NOI 入门组中非常常见,主要考察选手对数组下标转换的数学理解力,以及如何通过数学建模将复杂模拟简化为一维问题。


一、 预备知识点

  1. 二维数组与一维数组的映射:理解 m×nm \times n 矩阵中 (i,j)(i, j) 坐标与一维索引 idxidx 的转换公式:
    • 二维转一维:idx=i×n+jidx = i \times n + j
    • 一维转二维:i=idx/n,j=idx%ni = idx / n, \quad j = idx \% n
  2. 取模运算(Modulo)的周期性:处理环形移动或多次重复操作时,利用 k(modL)k \pmod L 简化计算。
  3. 矩阵模拟:基础的数组读写与遍历。

二、 题目描述 (NOI 竞赛风格)

题目名称:二维网格迁移 (Shift 2D Grid)

【问题描述】 给你一个 m×nm \times n 的二维网格 grid 和一个整数 kk。你需要对网格进行 kk 次迁移操作。 每次迁移操作逻辑如下:

  1. 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]
  2. 位于 grid[i][n - 1] 的元素将会移动到 grid[i + 1][0]
  3. 位于 grid[m - 1][n - 1] 的元素将会移动到 grid[0][0]

请输出 kk 次迁移操作后最终的二维网格。

【输入格式】 第一行包含三个正整数 m,n,km, n, k,分别表示行数、列数和迁移次数。 接下来 mm 行,每行包含 nn 个以空格分隔的整数,表示网格中的初始元素。

【输出格式】 输出 mm 行,每行包含 nn 个整数,表示迁移 kk 次后的网格。

【样例 1 输入】

3 3 1
1 2 3
4 5 6
7 8 9

【样例 1 输出】

9 1 2
3 4 5
6 7 8

【数据规模与约定】

  • 1m,n501 \le m, n \le 50
  • 1k1001 \le k \le 100
  • 1000grid[i][j]1000-1000 \le grid[i][j] \le 1000

三、 启发式引导:草稿纸上的推理过程

请拿起笔,我们在草稿纸上把这个二维的“蛇”拉直:

第一步:观察移动的本质

想象一个 3×33 \times 3 的矩阵,我们把它的每一行首尾相连:

(0,0) -> (0,1) -> (0,2) 
  ↓       (转行)
(1,0) -> (1,1) -> (1,2)
  ↓       (转行)
(2,0) -> (2,1) -> (2,2) --(回到)--> (0,0)

思考: 如果我们把这些位置按顺序排成一排,它像不像一个长度为 m×nm \times n 的一维数组? 在一次迁移中,每个元素只是向后移动了一格,最后一个元素跳到了最前面。

第二步:建立数学模型

  1. 扁平化:矩阵中位置 (i,j)(i, j) 对应一维序列中的第 idx=i×n+jidx = i \times n + j 个位置。
  2. 位移计算:移动 kk 次后,原本在 idxidx 位置的元素,现在走到了 (idx+k)(idx + k) 的位置。
  3. 处理越界:因为它是循环的,所以最终位置是 (idx+k)(modm×n)(idx + k) \pmod{m \times n}

第三步:复杂度分析与优化思考

  • 模拟法:如果你真的去写一个循环执行 kk 次,每次挪动所有元素,复杂度是 O(kmn)O(k \cdot m \cdot n)。虽然本题 kk 很小能过,但如果 k=109k = 10^9 呢?
  • 数学映射法:通过公式直接计算每个数迁移后的位置,时间复杂度为 O(mn)O(m \cdot n)。无论 kk 多大,只要先执行 k %= (m * n),速度都极快。
  • 空间复杂度:由于迁移是同步发生的,我们需要一个辅助阵列 res 来存放结果,空间复杂度 O(mn)O(m \cdot n)

四、 NOI 风格 C++14 伪代码提示

// 引入头文件 iostream, vector
使用命名空间 标准库;

常量整型 最大值 = 55;
整型 原始网格[最大值][最大值];
整型 结果网格[最大值][最大值];

函数 解决() {
    读取 m, n, k;
    
    // 总元素个数
    整型 总数 = m * n;
    // 关键优化:迁移总数倍数次等于没动
    k = k % 总数;

    对于 i 从 0 到 m-1:
        对于 j 从 0 到 n-1:
            读取 原始网格[i][j];

    // 遍历每一个原始位置
    对于 i 从 0 到 m-1:
        对于 j 从 0 到 n-1:
            // 1. 将二维坐标转为一维索引
            整型 旧索引 = i * n + j;
            
            // 2. 计算迁移后的新一维索引
            整型 新索引 = (旧索引 + k) % 总数;
            
            // 3. 将新索引转回二维坐标
            整型 新行 = 新索引 / n;
            整型 新列 = 新索引 % n;
            
            // 4. 放置元素
            结果网格[新行][新列] = 原始网格[i][j];

    // 输出结果网格...
}

五、 总结:读题关键词与题型识别

在 NOI 读题时,看到以下描述要立刻反应出“下标映射”模型:

  1. “行尾到下一行行首”:这明确提示了矩阵可以被视为一个连续的一维序列。
  2. “最后到最前”:这暗示了环形结构,必须使用取模运算
  3. “重复操作 kk 次”
    • 如果操作是独立的(如本题),直接计算最终偏移量 k(modL)k \pmod L
    • 如果操作互相依赖(如某些加密变换),寻找变换的周期。

教练点评: 这道题的精髓在于 “降维打击”。初学者容易陷入繁琐的 if-else 判断(比如判断是否是最后一列),但竞赛选手应优先思考坐标之间的数学关系。这种将二维结构映射到一维处理的技巧,是解决复杂矩阵问题的基石。加油,把代码逻辑在脑子里再跑一遍!