13.9 旋轉與翻轉

圖 13-4:順時針轉 90 度——舊圖 (i, j) 跑到新圖 (j, R-1-i)

旋轉和翻轉都是「每一格搬到新位置」,關鍵只有一條座標公式。拿 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。給定 BR \times C1 \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 的型態別名。旋轉會讓長寬對調,所以每次都要重新開一張新表再搬過去(不能在原地轉);輸出時的列數行數也要用還原後的大小。