Programare Competitivă

Rotiri și transformări de matrici

În multe probleme trebuie să transformăm o matrice: să o rotim, să o oglindim sau să îi calculăm transpusa. Fiecare transformare urmează un tipar precis.


Transpusa

Transpusa unei matrici se obține interschimbând liniile cu coloanele: elementul a[i][j] devine a[j][i].

Original:          Transpusa:
1 2 3              1 4 7
4 5 6      →       2 5 8
7 8 9              3 6 9

Cod (matrice pătratică, in-place)

for (int i = 1; i < n; i++)
    for (int j = i + 1; j <= n; j++) {
        int aux = a[i][j];
        a[i][j] = a[j][i];
        a[j][i] = aux;
    }

Parcurgem doar deasupra diagonalei (j > i) și interschimbăm cu elementul simetric.

Cod (matrice dreptunghiulară, cu matrice nouă)

int b[101][101];

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        b[j][i] = a[i][j];

// b are m linii și n coloane

Oglindire orizontală (sus-jos)

Interschimbăm linia 1 cu linia n, linia 2 cu linia n-1, etc.

Original:          Oglindit:
1 2 3              7 8 9
4 5 6      →       4 5 6
7 8 9              1 2 3
for (int i = 1; i <= n / 2; i++)
    for (int j = 1; j <= m; j++) {
        int aux = a[i][j];
        a[i][j] = a[n + 1 - i][j];
        a[n + 1 - i][j] = aux;
    }

Oglindire verticală (stânga-dreapta)

Interschimbăm coloana 1 cu coloana m, coloana 2 cu coloana m-1, etc.

Original:          Oglindit:
1 2 3              3 2 1
4 5 6      →       6 5 4
7 8 9              9 8 7
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m / 2; j++) {
        int aux = a[i][j];
        a[i][j] = a[i][m + 1 - j];
        a[i][m + 1 - j] = aux;
    }

Rotire 90° în sensul acelor de ceas

Elementul a[i][j] ajunge pe poziția b[j][n + 1 - i].

Original:          Rotit 90° →
1 2 3              7 4 1
4 5 6      →       8 5 2
7 8 9              9 6 3

Formulă

b[j][n + 1 - i] = a[i][j]

Cod

int b[101][101];

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        b[j][n + 1 - i] = a[i][j];

Alternativ (fără matrice auxiliară, matrice pătratică)

Rotim cadru cu cadru, de la exterior spre interior. La fiecare cadru, rotim 4 elemente simultan:

for (int k = 1; k <= n / 2; k++)
    for (int j = k; j < n + 1 - k; j++) {
        int aux = a[k][j];
        a[k][j] = a[n + 1 - j][k];
        a[n + 1 - j][k] = a[n + 1 - k][n + 1 - j];
        a[n + 1 - k][n + 1 - j] = a[j][n + 1 - k];
        a[j][n + 1 - k] = aux;
    }
Cum funcționează rotirea în 4 pași?

Fiecare element se mută pe poziția următoare, în cerc:

a[k][j] → temp
a[n+1-j][k] → a[k][j]
a[n+1-k][n+1-j] → a[n+1-j][k]
a[j][n+1-k] → a[n+1-k][n+1-j]
temp → a[j][n+1-k]

Cele 4 elemente formează un “ciclu” pe cerc. Le rotim pe toate simultan cu o variabilă auxiliară.


Rotire 90° în sens trigonometric (anti-ceas)

b[n + 1 - j][i] = a[i][j]

Original:          Rotit 90° ←
1 2 3              3 6 9
4 5 6      →       2 5 8
7 8 9              1 4 7
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        b[n + 1 - j][i] = a[i][j];

Rotire 180°

b[n + 1 - i][m + 1 - j] = a[i][j]

Echivalent cu: oglindit orizontal + oglindit vertical (sau invers).

Original:          Rotit 180°:
1 2 3              9 8 7
4 5 6      →       6 5 4
7 8 9              3 2 1
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        b[n + 1 - i][m + 1 - j] = a[i][j];

Tabel rezumativ formule

Transformare Formula: b[?][?] = a[i][j]
Transpusa b[j][i] = a[i][j]
Oglindire orizontală b[n+1-i][j] = a[i][j]
Oglindire verticală b[i][m+1-j] = a[i][j]
Rotire 90° ceas b[j][n+1-i] = a[i][j]
Rotire 90° anti-ceas b[n+1-j][i] = a[i][j]
Rotire 180° b[n+1-i][m+1-j] = a[i][j]

Relații utile: - Rotire 90° ceas = transpusă + oglindire verticală - Rotire 90° anti-ceas = transpusă + oglindire orizontală - Rotire 180° = oglindire orizontală + oglindire verticală


Exemplu complet: toate transformările

#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

int a[101][101], b[101][101];
int n;

void afiseaza(int mat[][101], int lin, int col) {
    for (int i = 1; i <= lin; i++) {
        for (int j = 1; j <= col; j++)
            fout << mat[i][j] << " ";
        fout << "\n";
    }
    fout << "\n";
}

int main()
{
    fin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            fin >> a[i][j];

    // Rotire 90° ceas
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            b[j][n + 1 - i] = a[i][j];

    fout << "Rotire 90 ceas:" << "\n";
    afiseaza(b, n, n);

    // Transpusa
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            b[j][i] = a[i][j];

    fout << "Transpusa:" << "\n";
    afiseaza(b, n, n);

    return 0;
}

Greșeli frecvente

1. Rotire in-place fără auxiliar

a[i][j] = a[j][i]; // am pierdut valoarea veche a lui a[i][j]!

Folosește matrice auxiliară b sau interschimbare cu aux.


2. Formula greșită la rotire

Cele mai confundate: 90° ceas vs anti-ceas. Testează mereu pe o matrice mică (2x2 sau 3x3).


3. Rotire pe matrice dreptunghiulară

Rotirea 90° a unei matrici n x m produce o matrice m x n. Dimensiunile se schimbă.


4. Oglindire pe jumătate

La oglindire, parcurgem doar jumătate din matrice (n/2 linii sau m/2 coloane). Altfel, interschimbăm de două ori și revenim la original.


Ce să reții

  • Transpusa: interschimbăm a[i][j] cu a[j][i].
  • Oglindire: interschimbăm linii (orizontală) sau coloane (verticală).
  • Rotire 90°: folosim formula b[j][n+1-i] = a[i][j] (ceas) sau b[n+1-j][i] = a[i][j] (anti-ceas).
  • Rotire 180°: b[n+1-i][m+1-j] = a[i][j].
  • Folosește matrice auxiliară sau interschimbare cu aux.
  • Rotirea schimbă dimensiunile pe matrici dreptunghiulare.