13.9 旋轉與翻轉
旋轉和翻轉都是「每一格搬到新位置」,關鍵只有一條座標公式。拿 R \times C 的矩陣 A 來說:
| 操作 | 舊圖 (i, j) 搬到 | 新圖大小 | 程式 |
|---|---|---|---|
| 上下翻轉 | (R - 1 - i,\ j) | R \times C | reverse(A.begin(), A.end());(整列對調) |
| 順時針轉 90^\circ | (j,\ R - 1 - i) | C \times R | 新開一張 B(C, vector<int>(R)),B[j][R-1-i] = A[i][j] |
| 逆時針轉 90^\circ | (C - 1 - j,\ i) | C \times R | 把上一條反過來讀:B[i][j] = A[j][C-1-i] |
公式怎麼來的?拿 2 \times 3 的小矩陣手畫一次:順時針轉 90^\circ 後,舊的第 0 列(最上面一排)變成新的最右邊一直排、舊的最後一列變成新的最左邊一直排——所以舊的第 i 列跑去新的第 R - 1 - i 行;而舊的第 j 行(由左往右)變成新的第 j 列(由上往下)。不確定就畫,公式馬上對得出來。
¶例題:矩陣轉換(APCS 2016 年 3 月中級)
題目:矩陣有兩種操作——翻轉(第一列與最後一列交換、第二列與倒數第二列交換……)與旋轉(順時針轉 90^\circ)。矩陣 A 經過一連串操作變成 B。給定 B(R \times C,1 \le R, C, M \le 10)和 M 個操作(0 旋轉、1 翻轉,依施作順序),請算出原始的矩陣 A:先輸出 A 的列數行數,再輸出內容。
題目給的是做完之後的樣子,要還原就得從最後一個操作倒著做「反操作」:翻轉的反操作還是翻轉;順時針旋轉的反操作是逆時針旋轉。
#include <bits/stdc++.h>
using namespace std;
using Matrix = vector<vector<int>>;
int main() {
int rows, cols, operationCount;
cin >> rows >> cols >> operationCount;
Matrix matrix(rows, vector<int>(cols));
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++) cin >> matrix[i][j];
vector<int> operations(operationCount);
for (int k = 0; k < operationCount; k++) cin >> operations[k];
for (int k = operationCount - 1; k >= 0; k--) { // 從最後一個操作倒著還原
if (operations[k] == 1) {
reverse(matrix.begin(), matrix.end()); // 翻轉:整排上下對調
} else {
rows = (int)matrix.size();
cols = (int)matrix[0].size();
Matrix restored(cols, vector<int>(rows)); // 逆時針旋轉 90 度:大小變成 cols x rows
for (int i = 0; i < cols; i++)
for (int j = 0; j < rows; j++)
restored[i][j] = matrix[j][cols - 1 - i];
matrix.swap(restored);
}
}
rows = (int)matrix.size();
cols = (int)matrix[0].size();
cout << rows << ' ' << cols << '\n';
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (j) cout << ' ';
cout << matrix[i][j];
}
cout << '\n';
}
return 0;
}
using Matrix = vector<vector<int>>; 是 12.7 的型態別名。旋轉會讓長寬對調,所以每次都要重新開一張新表再搬過去(不能在原地轉);輸出時的列數行數也要用還原後的大小。