旋转矩阵-多次操作模拟

题目描述

题目链接:N诺/DreamJudge 1221

给定一个 n×m 矩阵和 k 次操作,按顺序执行后输出最终矩阵。三种操作:

操作 含义
1 顺时针旋转 90°
2 沿纵向对称轴翻折(左右翻转)
3 逆时针旋转 90°

本题最关键的区别:矩阵是 n×m,不是 n×n。

核心思路

和 n×n 方阵的最大区别:旋转 90° 后,行数和列数会交换

旋转前的 n×m → 旋转后变成 m×n。忘记交换 n 和 m,后续操作就会在错误的维度上进行。

操作 1:顺时针旋转 90°

公式和方阵版一样,但新矩阵的大小是 m×n

1
2
3
4
5
6
7
8
9
vector<vector<int>> op1(vector<vector<int>> mtx, int n, int m) {
vector<vector<int>> rotate(m, vector<int>(n)); // ⚠️ m行 n列!
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rotate[j][n - i - 1] = mtx[i][j];
}
}
return rotate;
}

调用后必须 swap(n, m),因为维度变了。

操作 2:纵向翻折

沿中轴线左右翻转,n 和 m 不变,直接原地交换即可:

1
2
3
4
5
6
7
8
vector<vector<int>> op2(vector<vector<int>> mtx, int n, int m) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m / 2; j++) {
swap(mtx[i][j], mtx[i][m - j - 1]); // ⚠️ 不是 n-j-1
}
}
return mtx;
}

操作 3:逆时针旋转 90°

不需要推导逆时针公式。逆时针 90° = 顺时针 90° 三次

1
2
3
4
5
6
vector<vector<int>> op3(vector<vector<int>> mtx, int n, int m) {
auto rotate = op1(mtx, n, m); // 顺时针一次
rotate = op1(rotate, m, n); // ⚠️ 传的是 m,n!
rotate = op1(rotate, n, m); // ⚠️ 又变回 n,m!
return rotate;
}

注意每次传给 op1 的 n 和 m 都在交替,因为中间结果矩阵的维度一直在变。

⚠️ 最关键的坑:swap(n, m)

每次执行操作 1 或操作 3 后,n 和 m 必须交换。否则下一步操作的矩阵维度全是错的:

1
2
3
4
5
6
7
8
9
10
11
for (int i = 0; i < k; i++) {
if (op[i] == 1) {
mtx = op1(mtx, n, m);
swap(n, m); // ⚠️ 转后维度交换
} else if (op[i] == 2) {
mtx = op2(mtx, n, m); // 翻折不改变维度
} else {
mtx = op3(mtx, n, m);
swap(n, m); // ⚠️ 逆时针转也会换维度
}
}

为什么 180°/270° 更容易踩坑

方阵(n=m)时 180° 不需要 swap,因为 n=n。但矩形矩阵中:

  • 顺时针 180° = op1(op1(mtx)) → n→m→n,维度绕一圈回来
  • 但在两次 op1 之间,中间结果的维度已经变了

所以 op3 里每次调 op1 都要传正确的 n,m,否则下标越界。


完整代码模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
using namespace std;

vector<vector<int>> op1(vector<vector<int>> mtx, int n, int m) {
vector<vector<int>> rotate(m, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rotate[j][n - i - 1] = mtx[i][j];
}
}
return rotate;
}

vector<vector<int>> op2(vector<vector<int>> mtx, int n, int m) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m / 2; j++) {
int temp = mtx[i][j];
mtx[i][j] = mtx[i][m - j - 1];
mtx[i][m - j - 1] = temp;
}
}
return mtx;
}

vector<vector<int>> op3(vector<vector<int>> mtx, int n, int m) {
auto rotate = op1(mtx, n, m);
rotate = op1(rotate, m, n);
rotate = op1(rotate, n, m);
return rotate;
}

int main() {
int n, m, k;
while (cin >> n >> m >> k) {
vector<vector<int>> mtx(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> mtx[i][j];
}
}
vector<int> op(k, 0);
for (int i = 0; i < k; i++) cin >> op[i];

for (int i = 0; i < k; i++) {
if (op[i] == 1) {
mtx = op1(mtx, n, m);
swap(n, m); // ⚠️ 顺时针转后维度交换
} else if (op[i] == 2) {
mtx = op2(mtx, n, m); // 翻折不换维度
} else {
mtx = op3(mtx, n, m);
swap(n, m); // ⚠️ 逆时针转后也要换
}
}

for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << mtx[i][j] << " ";
}
cout << endl;
}
}
}

总结

要点 说明
矩形旋转特点 n×m → m×n,维度交换
op1 后必 swap swap(n, m)
op3 后必 swap 等同于 3 次 op1,维度同样交换
op2 不 swap 翻折只是镜像,n,m 不变
op3 实现技巧 op1(op1(op1(mtx))),不用推逆时针公式
180° 中间态 op1 两次之间维度是 m×n,传参加倍小心