Programare Competitivă

Bordura și interiorul matricei

Multe probleme cer să procesăm doar anumite părți ale matricei: marginile (bordura) sau elementele din interior. În această lecție învățăm cum să le identificăm și parcurgem.


Ce este bordura?

Bordura (sau marginea) matricei conține elementele de pe:

  • Prima linie (i == 1)
  • Ultima linie (i == n)
  • Prima coloană (j == 1)
  • Ultima coloană (j == m)
Matrice 4 x 5:

[*] [*] [*] [*] [*]
[*]  .   .   .  [*]
[*]  .   .   .  [*]
[*] [*] [*] [*] [*]

Condiția

Un element a[i][j] este pe bordură dacă:

i == 1 || i == n || j == 1 || j == m

Afișarea bordurii

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        if (i == 1 || i == n || j == 1 || j == m)
            fout << a[i][j] << " ";

Suma bordurii

int suma = 0;

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

fout << suma;

Parcurgerea bordurii eficient (fără condiție)

În loc să verificăm fiecare element, parcurgem direct cele 4 laturi:

// Prima linie
for (int j = 1; j <= m; j++)
    fout << a[1][j] << " ";

// Ultima linie
for (int j = 1; j <= m; j++)
    fout << a[n][j] << " ";

// Prima coloana (fără colțuri, deja parcurse)
for (int i = 2; i < n; i++)
    fout << a[i][1] << " ";

// Ultima coloana (fără colțuri)
for (int i = 2; i < n; i++)
    fout << a[i][m] << " ";

Atenție la colțuri: dacă parcurgi separat cele 4 laturi, colțurile (a[1][1], a[1][m], a[n][1], a[n][m]) pot fi numărate de două ori. De aceea, la prima/ultima coloană începem de la i = 2 și terminăm la i = n - 1.


Ce este interiorul?

Interiorul conține toate elementele care nu sunt pe bordură:

 .   .   .   .   .
 .  [*] [*] [*]  .
 .  [*] [*] [*]  .
 .   .   .   .   .

Condiția

i > 1 && i < n && j > 1 && j < m

Sau mai simplu - parcurgem de la linia 2 la n-1 și coloana 2 la m-1:

for (int i = 2; i < n; i++)
    for (int j = 2; j < m; j++)
        fout << a[i][j] << " ";

Suma interiorului

int suma = 0;

for (int i = 2; i < n; i++)
    for (int j = 2; j < m; j++)
        suma = suma + a[i][j];

fout << suma;

Borduri concentrice (cadre)

Unele probleme cer să procesăm cadrul k al matricei - bordura de la distanța k de margine.

Cadrul 0 (bordura exterioară):
[*] [*] [*] [*] [*]
[*]  .   .   .  [*]
[*]  .   .   .  [*]
[*]  .   .   .  [*]
[*] [*] [*] [*] [*]

Cadrul 1:
 .   .   .   .   .
 .  [*] [*] [*]  .
 .  [*]  .  [*]  .
 .  [*] [*] [*]  .
 .   .   .   .   .

Cadrul k

Elementul a[i][j] aparține cadrului k dacă:

i == k + 1 || i == n - k || j == k + 1 || j == m - k

Și se află în interiorul cadrelor anterioare:

i >= k + 1 && i <= n - k && j >= k + 1 && j <= m - k

Parcurgerea cadrului k

void parcurgeCadru(int a[][101], int n, int m, int k) {
    // Linia de sus
    for (int j = k + 1; j <= m - k; j++)
        fout << a[k + 1][j] << " ";

    // Coloana din dreapta
    for (int i = k + 2; i <= n - k; i++)
        fout << a[i][m - k] << " ";

    // Linia de jos (invers)
    for (int j = m - k - 1; j >= k + 1; j--)
        fout << a[n - k][j] << " ";

    // Coloana din stânga (invers)
    for (int i = n - k - 1; i >= k + 2; i--)
        fout << a[i][k + 1] << " ";
}

Exemplu complet: verificare cadru magic

Verificăm dacă suma bordurii este egală cu suma interiorului.

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

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

int main()
{
    fin >> n >> m;

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

    int sumaBordura = 0;
    int sumaInterior = 0;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (i == 1 || i == n || j == 1 || j == m)
                sumaBordura = sumaBordura + a[i][j];
            else
                sumaInterior = sumaInterior + a[i][j];
        }

    fout << "Bordura: " << sumaBordura << endl;
    fout << "Interior: " << sumaInterior << endl;

    if (sumaBordura == sumaInterior)
        fout << "EGALE";
    else
        fout << "DIFERITE";

    return 0;
}

Modificarea bordurii

Setarea bordurii la o valoare

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

Incrementarea bordurii

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

Greșeli frecvente

1. Interiorul gol pe matrici mici

Dacă matricea are 1 sau 2 linii/coloane, nu are interior. Bucla for (int i = 2; i < n; ...) nu se execută.


2. Colțuri numărate dublu

Când parcurgi bordura linie cu linie + coloană cu coloană, colțurile apar de două ori. Folosește condiția unificată i == 1 || i == n || j == 1 || j == m sau ajustează limitele buclelor.


3. Confuzia n vs m

  • Liniile merg de la 1 la n
  • Coloanele merg de la 1 la m

La bordură: prima linie i == 1, ultima i == n, prima coloană j == 1, ultima j == m.


Ce să reții

  • Bordura: i == 1 || i == n || j == 1 || j == m.
  • Interiorul: i > 1 && i < n && j > 1 && j < m (sau buclă de la 2 la n-1).
  • Parcurgerea eficientă a bordurii: cele 4 laturi separat, atenție la colțuri.
  • Cadrele concentrice: borduri la distanță k de margine.
  • Pe matrici mici (1x1, 2x2), interiorul poate fi gol.