矩阵重塑(其二)

用一维行优先序列保存矩阵,重塑只修改形状,转置时按下标映射重排,查询直接定位线性下标。

OJ: shumeng

题目 ID: CSP202406B

难度:普及-

标签:模拟矩阵下标映射

日期: 2026-07-31 16:21

形式化题目

给定一个初始 n×mn \times m 矩阵,支持三类操作:

  1. 重塑为 x×yx \times y:按行优先顺序重排为新的行列形状;
  2. 转置:交换行和列;
  3. 查询 (x,y)(x, y):输出该位置的元素。

所有操作都保持元素总数不变,且操作保证合法。

思路

关键区分:重塑不改变行优先线性序列,只改形状;转置才真正改变行优先序列

朴素做法:直接维护二维矩阵

先看直观实现,直接用二维数组保存矩阵,每次操作都显式地在二维下标间搬运元素。

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-31 16:21
 * update_at: 2026-08-17 22:39
 */
// brute.cpp:小数据暴力解,直接维护二维矩阵完成重塑、转置与查询。
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 10005;

int matrix[MAXN][MAXN]; // 直接保存二维矩阵
int n, m;

// 重塑为 x 行 y 列:先按行优先取出所有元素,再按新形状放回
void reshape(int x, int y) {
    int temp[MAXN];
    int count = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) temp[count++] = matrix[i][j];
    }
    count = 0;
    for (int i = 0; i < x; i++) {
        for (int j = 0; j < y; j++) matrix[i][j] = temp[count++];
    }
    n = x;
    m = y;
}

// 转置:新矩阵的 (j,i) 位置放原矩阵的 (i,j)
void transpose() {
    int temp[MAXN]; // 平铺存放转置后的元素
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            temp[j * n + i] = matrix[i][j];
        }
    }
    int count = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) matrix[i][j] = temp[count++];
    }
    swap(n, m);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int operation_count;
    cin >> n >> m >> operation_count;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) cin >> matrix[i][j];
    }

    while (operation_count--) {
        int type, x, y;
        cin >> type >> x >> y;
        if (type == 1) {
            reshape(x, y);
        } else if (type == 2) {
            transpose();
        } else {
            cout << matrix[x][y] << '\n';
        }
    }

    return 0;
}

主解:一维序列 + 下标映射

用一维数组 value 按行优先顺序保存所有元素,同时维护当前行列数 n,mn, m

  • 重塑:只修改 n,mn, m,数组不动,O(1)O(1)
  • 查询:位置 (x,y)(x, y) 的线性下标是 x×m+yx \times m + y,直接输出。
  • 转置:原位置 (i,j)(i, j) 线性下标 i×m+ji \times m + j,转置后变为 (j,i)(j, i),新线性下标 j×n+ij \times n + i。扫描一遍数组按新下标重排,并交换 n,mn, m

由于元素总数不超过 10410^4 且转置次数不超过 100100,每次转置线性扫描一次完全可行。

代码

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-31 16:21
 * update_at: 2026-08-17 22:39
 */
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 10005;

int value[MAXN]; // 行优先保存的矩阵元素
int n, m;        // 当前矩阵的行数与列数

// 矩阵转置:原位置 (i,j) 的线性下标 i*m+j,转置后位置 (j,i) 的线性下标 j*n+i
void transpose() {
    int temp[MAXN];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            temp[j * n + i] = value[i * m + j];
        }
    }
    for (int i = 0; i < n * m; i++) value[i] = temp[i];
    swap(n, m);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int operation_count;
    cin >> n >> m >> operation_count;
    for (int i = 0; i < n * m; i++) cin >> value[i];

    while (operation_count--) {
        int type, x, y;
        cin >> type >> x >> y;
        if (type == 1) {
            n = x; // 重塑:只改形状,行优先序列不变
            m = y;
        } else if (type == 2) {
            transpose(); // 转置:需要重排元素并交换行列
        } else {
            cout << value[x * m + y] << '\n';
        }
    }

    return 0;
}

复杂度

设矩阵元素总数为 N=nmN = nm

  • 时间:初始读入 O(N)O(N);转置 O(N)O(N);重塑与查询 O(1)O(1)。转置最多 100100 次,总时间复杂度 O(N+100N+t)O(N + 100N + t)
  • 空间:一维数组保存全部元素,空间复杂度 O(N)O(N)

总结

把“形状变化”和“元素重排”分开处理就不会混淆:重塑只是换一种方式解释同一串数据,转置才需要真正的下标重排。用一维行优先序列配合 i×m+ji \times m + j 的线性下标公式,三类操作都能高效实现。