Tablouri bidimensionale (matrice)
Elevii lucrează cu structuri de date bidimensionale și le prelucrează prin parcurgere pe linii și coloane.
Ce este o matrice și cum o declarăm
O matrice (tablou bidimensional) este un tabel de valori de același tip, organizat pe linii și coloane — exact ca un orar școlar sau ca o tablă de șah. Dacă vectorul avea un singur indice, matricea are doi: primul arată linia, al doilea arată coloana.
În C++, declararea arată așa: int a[20][30]; — o matrice cu cel mult 20 de linii și 30 de coloane, cu elemente întregi. Elementul de pe linia i și coloana j se accesează cu a[i][j] — și se comportă ca orice variabilă obișnuită.
Dimensiunile efectiv folosite se citesc separat: n (numărul de linii) și m (numărul de coloane). O matrice cu n linii și m coloane are în total n × m elemente.
Convenția de ordine este sfântă și trebuie repetată până devine reflex: întâi linia, apoi coloana. a[3][5] este elementul de pe linia 3, coloana 5 — nu invers! Inversarea indicilor este greșeala numărul unu la acest capitol: programul compilează, dar prelucrează cu totul alte elemente.
Ca și la vectori, indexarea în C++ începe de la 0 (prima linie este linia 0), dar multe probleme numerotează de la 1 — verifică mereu convenția din enunț. Și tot ca la vectori, accesarea unei poziții din afara matricei nu este semnalată de compilator, dar strică execuția.
Cazuri particulare cu nume proprii: matricea cu o singură linie sau o singură coloană seamănă cu un vector; matricea cu n = m se numește matrice pătratică și are proprietăți speciale, discutate mai jos.
Parcurgerea matricei: două bucle imbricate
Pentru a vizita toate elementele matricei ai nevoie de două bucle for imbricate — una pentru linii, una pentru coloane:
for (i = 0; i < n; i++) for (j = 0; j < m; j++) cin >> a[i][j];
Cu bucla exterioară pe i și cea interioară pe j, parcurgerea se face pe linii: se vizitează complet linia 0 (element cu element, de la stânga la dreapta), apoi linia 1 și așa mai departe. Este ordinea firească de citire și de afișare.
Dacă inversezi buclele — exteriorul pe j, interiorul pe i — parcurgerea se face pe coloane: întâi toată coloana 0, de sus în jos, apoi coloana 1... Elementele vizitate sunt aceleași, dar ordinea diferă, iar la afișare rezultatul arată complet altfel.
La afișarea sub formă de tabel, detaliul care face diferența este trecerea la rând nou: după fiecare linie completă (adică după terminarea buclei interioare) se afișează endl sau '\n'. Dacă pui rândul nou în interiorul buclei interioare, fiecare element apare pe rândul lui; dacă îl uiți complet, toată matricea se afișează pe un singur rând.
Numărul total de pași: bucla interioară face m pași pentru fiecare dintre cei n pași ai buclei exterioare, deci corpul se execută de n × m ori — regula generală a buclelor imbricate.
Greșeala clasică de imbricare rămâne valabilă: cele două bucle au contoare diferite (i și j); refolosirea aceluiași contor face parcurgerea haotică.
Operații pe linii și pe coloane
Multe cerințe se referă doar la o anumită linie sau o anumită coloană — și aici se vede dacă ai înțeles rolul celor doi indici:
- Suma elementelor de pe linia k: linia e fixă, coloana variază — s = 0; for (j = 0; j < m; j++) s += a[k][j]; Observă: o singură buclă, pentru că un indice e fixat;
- Suma elementelor de pe coloana k: coloana e fixă, linia variază — for (i = 0; i < n; i++) s += a[i][k];
- Maximul de pe fiecare linie: pentru fiecare i, aplici șablonul de maxim de la vectori pe linia i (inițializezi cu a[i][0], primul element al liniei — nu cu 0!);
- Suma pe fiecare linie: aici acumulatorul se reinițializează la începutul fiecărei linii, adică în interiorul buclei exterioare, dar înaintea celei interioare. Poziția inițializării este exact ce testează problemele: s = 0 pus înaintea ambelor bucle dă suma întregii matrice, nu suma fiecărei linii.
Alte prelucrări frecvente:
- interschimbarea a două linii p și q: interschimbi a[p][j] cu a[q][j] pentru fiecare j — element cu element, cu variabilă auxiliară;
- numărarea elementelor cu o proprietate dintr-o linie, coloană sau din toată matricea — același șablon de numărare de la vectori, cu una sau două bucle după caz.
Regula practică de citire a enunțului: „pe linia k” → k e primul indice, fix; „pe coloana k” → k e al doilea indice, fix. Cine fixează indicele greșit calculează pe coloană în loc de linie și pierde toate punctele cerinței.
Matricea pătratică și diagonalele
Matricea pătratică are numărul de linii egal cu numărul de coloane (n = m). Doar la matrice pătratice vorbim despre diagonale:
- Diagonala principală unește colțul stânga-sus cu colțul dreapta-jos și conține elementele cu indicii egali: a[0][0], a[1][1], ..., a[n-1][n-1]. Condiția de apartenență: i == j. Suma ei se calculează cu o singură buclă: for (i = 0; i < n; i++) s += a[i][i];
- Diagonala secundară unește colțul dreapta-sus cu colțul stânga-jos. Cu indexare de la 0, condiția este i + j == n - 1, iar elementele sunt a[0][n-1], a[1][n-2], ..., a[n-1][0]. (Cu indexare de la 1, condiția devine i + j == n + 1 — încă un motiv să verifici convenția enunțului.)
Diagonalele împart matricea pătratică în zone care apar des în cerințe:
- elementele de deasupra diagonalei principale: i < j;
- elementele de sub diagonala principală: i > j;
- pentru diagonala secundară: deasupra înseamnă i + j < n - 1, dedesubt i + j > n - 1.
Capcană de numărare: cele două diagonale au câte n elemente fiecare, dar dacă n este impar, ele se intersectează în elementul din centru (a[n/2][n/2]), care aparține amândurora — la „suma elementelor de pe ambele diagonale” acest element nu trebuie adunat de două ori.
Tot la matrice pătratice se definește transpusa: matricea obținută prin transformarea liniilor în coloane, adică interschimbarea lui a[i][j] cu a[j][i]. O matrice egală cu transpusa ei (a[i][j] == a[j][i] peste tot) se numește simetrică față de diagonala principală.
Probleme aplicative tipice
Tiparele de probleme cu matrice combină șabloanele de la vectori cu geometria tabelului:
- Suma/maximul/numărarea pe toată matricea: două bucle imbricate plus acumulatorul cunoscut. Maximul se inițializează cu a[0][0], primul element al matricei;
- Prelucrări pe zone: „elementele pare de sub diagonala principală” → două bucle plus if cu condiție compusă (i > j && a[i][j] % 2 == 0). Enunțurile de acest tip testează dacă știi să combini condiția de zonă cu condiția de valoare;
- Marginile matricei (chenarul): un element este pe margine dacă i == 0 sau i == n-1 sau j == 0 sau j == m-1. Interiorul este exact opusul: toate cele patru condiții false;
- Linia cu suma maximă: calculezi suma fiecărei linii (acumulator resetat pe linie), apoi aplici șablonul de maxim pe aceste sume, memorând indicele liniei câștigătoare;
- Verificări cu steag: „matricea este simetrică?” — pornești cu ok = 1 și îl faci 0 la prima pereche cu a[i][j] != a[j][i];
- Generarea matricelor: unele probleme cer construirea matricei după o regulă, fără citire — de exemplu a[i][j] = i + j, sau matricea unitate (1 pe diagonala principală, 0 în rest: a[i][j] = (i == j)).
Strategia de rezolvare rămâne aceeași ca la vectori, cu un pas în plus: întâi stabilești ce zonă a matricei parcurgi (toată? o linie? o diagonală? sub diagonală?), apoi alegi numărul de bucle (două pentru zone bidimensionale, una singură pentru o linie, o coloană sau o diagonală) și abia apoi aplici șablonul de prelucrare. Multe greșeli vin din folosirea a două bucle acolo unde zona cere doar una.
De reținut
- matrice (tablou bidimensional)
- tabel de elemente de același tip organizat pe linii și coloane, accesate prin doi indici: a[i][j], întâi linia, apoi coloana
- parcurgere pe linii
- vizitarea elementelor linie cu linie, cu bucla exterioară pe indicele de linie și cea interioară pe indicele de coloană
- parcurgere pe coloane
- vizitarea elementelor coloană cu coloană, cu bucla exterioară pe indicele de coloană și cea interioară pe indicele de linie
- matrice pătratică
- matrice cu numărul de linii egal cu numărul de coloane; doar pentru ea se definesc diagonalele
- diagonala principală
- elementele cu indici egali (i == j), de la colțul stânga-sus la colțul dreapta-jos al unei matrice pătratice
- diagonala secundară
- elementele cu i + j == n - 1 (indexare de la 0), de la colțul dreapta-sus la colțul stânga-jos al unei matrice pătratice
- zona de sub diagonala principală
- elementele cu i > j; cele de deasupra diagonalei principale au i < j
- transpusa unei matrice
- matricea obținută prin transformarea liniilor în coloane; matricea egală cu transpusa ei se numește simetrică
- element de pe chenar (margine)
- element aflat pe prima sau ultima linie ori pe prima sau ultima coloană: i == 0 sau i == n-1 sau j == 0 sau j == m-1
Greșeli frecvente
Greșit: Inversarea indicilor: folosirea lui a[j][i] sau fixarea primului indice pentru o coloană
Corect: Primul indice este întotdeauna linia, al doilea coloana; pentru linia k fixată variază al doilea indice (a[k][j]), pentru coloana k fixată variază primul (a[i][k])
Greșit: Inițializarea sumei pe fiecare linie o singură dată, înaintea ambelor bucle
Corect: Pentru suma fiecărei linii, s = 0 se scrie în interiorul buclei exterioare, înaintea celei interioare; pusă înaintea ambelor bucle, se obține suma întregii matrice
Greșit: Adunarea de două ori a elementului central la suma ambelor diagonale, când n este impar
Corect: La n impar, diagonalele se intersectează în a[n/2][n/2]; elementul comun se numără o singură dată sau se scade o dată din total
Greșit: Folosirea condiției i + j == n - 1 pentru diagonala secundară indiferent de convenția de indexare
Corect: Condiția depinde de convenție: i + j == n - 1 la indexare de la 0, dar i + j == n + 1 la indexare de la 1 — se verifică enunțul înainte de a scrie condiția
Greșit: Parcurgerea unei diagonale cu două bucle imbricate și if, considerată singura variantă
Corect: O diagonală are doar n elemente și se parcurge eficient cu o singură buclă (a[i][i] sau a[i][n-1-i]); varianta cu două bucle face n × n pași pentru aceleași n elemente
Test — 6 întrebări ca la examen
1. Într-o matrice a cu 4 linii și 5 coloane, elementul de pe linia 2, coloana 3 (indexare de la 0) se accesează cu:
- a[3][2]
- a[2][3]
- a[2, 3]
- a[3, 2]
Vezi răspunsul
a[2][3]. Convenția este întâi linia, apoi coloana, fiecare în propriile paranteze drepte: a[2][3]. Distractorul a[3][2] inversează indicii — cea mai frecventă greșeală la matrice — iar formele cu virgulă nu reprezintă în C++ accesul dorit la element.
2. Câte elemente are în total o matrice cu n = 6 linii și m = 4 coloane și de câte ori se execută corpul celor două bucle imbricate care o citesc?
- 10 elemente, 10 execuții
- 24 de elemente, 24 de execuții
- 6 elemente, 4 execuții
- 24 de elemente, 10 execuții
Vezi răspunsul
24 de elemente, 24 de execuții. Matricea are n × m = 24 de elemente, iar buclele imbricate execută corpul exact o dată pentru fiecare element, deci tot de 24 de ori. Distractorul cu 10 vine din adunarea dimensiunilor (6 + 4) în loc de înmulțirea lor.
3. Care secvență calculează corect suma elementelor de pe coloana k a unei matrice cu n linii și m coloane?
- s = 0; for (j = 0; j < m; j++) s += a[k][j];
- s = 0; for (i = 0; i < n; i++) s += a[i][k];
- s = 0; for (i = 0; i < n; i++) s += a[k][i];
- s = 0; for (i = 0; i < n; i++) for (j = 0; j < m; j++) s += a[i][j];
Vezi răspunsul
s = 0; for (i = 0; i < n; i++) s += a[i][k];. Pe o coloană fixă, al doilea indice rămâne k și variază linia: a[i][k]. Prima variantă — distractorul simetric — fixează primul indice, deci adună linia k, nu coloana k; ultima adună toată matricea.
4. Într-o matrice pătratică indexată de la 0, elementele diagonalei principale sunt cele pentru care:
- i + j == n - 1
- i == j
- i > j
- i + j == n + 1
Vezi răspunsul
i == j. Diagonala principală leagă colțul stânga-sus de colțul dreapta-jos, iar pe ea indicii sunt egali: i == j. Condiția i + j == n - 1 — distractorul tentant — descrie diagonala secundară; cele două se confundă constant.
5. Pentru matricea pătratică 3×3 cu liniile (1, 2, 3), (4, 5, 6), (7, 8, 9), care este suma elementelor aflate pe ambele diagonale, fiecare element numărat o singură dată?
- 30
- 25
- 15
- 20
Vezi răspunsul
25. Diagonala principală: 1 + 5 + 9 = 15; diagonala secundară: 3 + 5 + 7 = 15. Elementul central 5 apare pe ambele, deci se numără o dată: 15 + 15 - 5 = 25. Distractorul 30 adună pur și simplu cele două sume, dublând centrul — capcana clasică pentru n impar.
6. Ce afișează secvența: for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) cout << a[i][j] << ' '; cout << endl; } pentru matricea cu liniile (1, 2, 3) și (4, 5, 6)?
- 1 4 / 2 5 / 3 6, pe trei rânduri
- 1 2 3 4 5 6, pe un singur rând
- 1 2 3 pe primul rând și 4 5 6 pe al doilea
- fiecare element pe propriul rând
Vezi răspunsul
1 2 3 pe primul rând și 4 5 6 pe al doilea. Bucla exterioară pe i și cea interioară pe j dau parcurgere pe linii, iar endl aflat după bucla interioară trece la rând nou după fiecare linie completă — afișare de tabel corectă. Varianta cu trei rânduri descrie parcurgerea pe coloane, iar celelalte două corespund lui endl lipsă ori pus în bucla interioară.
Deschide varianta interactivă — cu AI care îți explică