Atestia

Matrici (tablouri bidimensionale) la atestatul de informatică

Citirea unei matrici din fișier, parcurgerea pe linii și pe coloane, diagonalele, maximul cu poziții și verificarea unei proprietăți pe toată linia — cu problemele de atestat în care apar.

La atestat, matricile vin aproape întotdeauna dintr-un fișier, nu de la tastatură. Toate cele 9 probleme din sesiunea Dolj 2026 folosesc același fişier mat.in și același format. Odată ce memorezi scheletul de citire, rămâne doar logica fiecărei probleme.

Scheletul de citire din fișier

Se repetă identic la toate problemele:

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ifstream f("mat.in");

    int n;
    f >> n;

    int a[20][20];
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            f >> a[i][j];
    f.close();
    // ...
}

ifstream e „ruda" lui cin pentru fișiere — se folosește la fel, cu >>. Trebuie doar deschis cu numele fişierului și închis la final.

Formatul lui mat.in e mereu același: pe prima linie n, apoi n linii cu câte n valori.

Atenție la sursa datelor. Unele enunțuri citesc matricea din fişier, dar parametrii (de exemplu liniile de interschimbat) de la tastatură. Sunt fluxuri separate: f >> pentru fişier, cin >> pentru tastatură.

Tipul urmează enunțul: „numere întregi" → int, „numere reale" → double.

Linii și coloane: ce decide parcurgerea

Acesta e conceptul cel mai important din toată categoria, și cel mai des greșit.

// pe LINII: fixezi linia i, plimbi coloana j
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) ... a[i][j]

// pe COLOANE: fixezi coloana j, plimbi linia i
for (int j = 0; j < n; j++)
    for (int i = 0; i < n; i++) ... a[i][j]

Observă că a[i][j] rămâne neschimbat în ambele cazuri. Primul indice e întotdeauna linia, al doilea întotdeauna coloana. Ce se schimbă e doar ordinea buclelor.

Greșeala clasică e să scrii a[j][i] în loc să inversezi buclele. Pe o matrice pătratică programul rulează fără eroare, dar lucrezi pe transpusă — obții liniile când ai cerut coloanele.

Poziții vs. indici. Enunțurile numără liniile și coloanele de la 1, C++ de la 0. Elementul a[0][1] se raportează ca l=1 c=2.

Diagonalele

Merită memorate — apar la multe subiecte:

Diagonala Formulă Direcție Pentru n = 3
principală a[i][i] stânga-sus → dreapta-jos a[0][0], a[1][1], a[2][2]
secundară a[i][n - 1 - i] dreapta-sus → stânga-jos a[0][2], a[1][1], a[2][0]

Pentru diagonala secundară, pe măsură ce linia crește, coloana scade — de aici n - 1 - i. Scrierea greșită a[i][n - i] iese din matrice chiar la primul pas.

O singură buclă e suficientă pentru o diagonală: are exact n elemente, câte unul pe fiecare linie. Nu ai nevoie de parcurgere completă cu if.

Probleme: media pe diagonala secundară

Tehnica 1 — Valoare extremă și pozițiile ei

Când enunțul cere și valoarea maximă, și unde apare, ai nevoie de două parcurgeri:

double maxim = a[0][0];                 // primul element, NU 0
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        if (a[i][j] > maxim) maxim = a[i][j];

for (int i = 0; i < n; i++)             // abia acum stim ce cautam
    for (int j = 0; j < n; j++)
        if (a[i][j] == maxim)
            cout << "l=" << i + 1 << " c=" << j + 1 << endl;

Motivul: ca să știi dacă o poziție conține maximul, trebuie să-l cunoști deja — dar el poate apărea abia la ultimul element.

Inițializarea cu a[0][0], nu cu 0, e obligatorie: 0 e greșit pentru matrici cu toate valorile negative (la maxim) sau toate pozitive (la minim).

Pentru minim se schimbă doar semnul comparației.

Probleme: elementul maxim și pozițiile · elementul minim și pozițiile · maximele de pe fiecare linie

Când enunțul cere un rezultat per linie, calculul stă în bucla exterioară, iar acumulatorul se redeclară la fiecare linie — altfel se „târăște" de pe o linie pe alta.

Tehnica 2 — „Toate elementele respectă o proprietate"

Aici logica se inversează față de problemele obișnuite.

Ca să demonstrezi că toate elementele unei linii sunt pare, trebuie să le verifici pe toate. Dar ca să demonstrezi că nu sunt, e suficient un singur contraexemplu:

bool toatePare = true;              // pornesti OPTIMIST
for (int j = 0; j < n; j++)
    if (a[i][j] % 2 != 0) {
        toatePare = false;          // un contraexemplu strica tot
        break;
    }

Compară cu tiparul obișnuit „există un element care...", unde pornești cu false și cauți o confirmare. Confuzia dintre cele două e cea mai frecventă greșeală de logică la matrici.

Flagul trebuie declarat în interiorul buclei de linii, ca să se reseteze pentru fiecare linie.

Probleme: liniile cu toate elementele pare · coloanele cu toate elementele impare · matrice simetrică

Matrice simetrică

O matrice e simetrică față de diagonala principală dacă a[i][j] == a[j][i] pentru orice pereche. Se verifică doar deasupra diagonalei:

for (int i = 0; i < n && simetrica; i++)
    for (int j = i + 1; j < n; j++)      // j porneste de la i + 1
        if (a[i][j] != a[j][i]) { simetrica = false; break; }

Perechile de sub diagonală sunt aceleași verificate a doua oară, iar elementele de pe diagonală se compară cu ele însele. Jumătate din muncă, niciun test inutil.

Tehnica 3 — Medie condiționată

Ai nevoie de doi acumulatori: suma și numărătoarea.

double suma = 0;
int cate = 0;
for (...)
    if (a[i][j] > 0) { suma += a[i][j]; cate++; }

if (cate > 0) cout << suma / cate;
else cout << "NU EXISTA";

Media e suma / cate, nu suma / (n * n) — împarți la câte elemente ai selectat, nu la totalul din matrice. Iar verificarea cate > 0 previne împărțirea la zero.

„Strict pozitiv" înseamnă > 0; zeroul nu intră.

Probleme: media elementelor strict pozitive

Tehnica 4 — Interschimbarea a două linii

O linie nu e o valoare, ci n valori. Nu poți scrie a[l1] = a[l2] — muți element cu element:

for (int j = 0; j < n; j++) {
    double aux = a[l1 - 1][j];
    a[l1 - 1][j] = a[l2 - 1][j];
    a[l2 - 1][j] = aux;
}

Indicii de linie rămân fixați, bucla plimbă coloana. Pentru coloane e exact invers: bucla merge pe i, iar indicii de coloană rămân constanți.

Probleme: interschimbarea a două linii

Afișarea unei matrici

for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++)
        cout << a[i][j] << " ";
    cout << endl;                       // in bucla EXTERIOARA
}

cout << endl stă după ce s-a terminat o linie întreagă. În bucla interioară, fiecare element ar apărea pe rândul lui.

Greșeli care se repetă

Toate problemele din această categorie

Fiecare are rezolvare completă în C++, explicată pas cu pas.