Ce este Backtracking?
Imaginează-ți că ești într-un labirint și trebuie să găsești ieșirea:
- Mergi pe un drum → dacă ajungi la un blocaj, dai înapoi (backtrack)
- Încerci altă direcție → dacă și aceea e blocată, dai înapoi din nou
- Continui până găsești ieșirea sau ai încercat toate drumurile
Backtracking-ul construiește soluția pas cu pas, și dacă o decizie duce la un eșec, se întoarce și încearcă altă variantă.
Backtracking este o tehnică algoritmică de căutare exhaustivă sistematică cu posibilitatea de a reveni (backtrack) atunci când o soluție parțială nu poate fi extinsă la o soluție completă validă.
Generăm toate soluțiile posibile (permutări, combinări, submulțimi)
Găsim soluția optimă care respectă anumite restricții
Verificăm dacă există cel puțin o soluție (labirint, sudoku)
Șablonul general Backtracking
void backtracking(int nivel) { if (solutie_completa(nivel)) { afiseazaSolutie(); return; } for (fiecare valoare posibila v) { if (esteValid(nivel, v)) { stiva[nivel] = v; // alege valoarea backtracking(nivel + 1); // explorează mai departe stiva[nivel] = 0; // BACKTRACK — dezalege } } }
Generarea permutărilor
Generăm toate aranjamentele posibile ale elementelor {1, 2, ..., n}. Numărul de permutări = n!
int n, st[20]; bool folosit[20]; void permutari(int nivel) { if (nivel == n + 1) { for (int i = 1; i <= n; i++) cout << st[i] << " "; cout << endl; return; } for (int v = 1; v <= n; v++) { if (!folosit[v]) { st[nivel] = v; folosit[v] = true; permutari(nivel + 1); st[nivel] = 0; // backtrack folosit[v] = false; // backtrack } } }
Generarea combinărilor
Generăm toate submulțimile de câte k elemente din {1, 2, ..., n}. Numărul de combinări = C(n,k) = n! / (k! × (n-k)!)
int n, k, st[20]; void combinari(int nivel, int startVal) { if (nivel == k + 1) { for (int i = 1; i <= k; i++) cout << st[i] << " "; cout << endl; return; } for (int v = startVal; v <= n; v++) { st[nivel] = v; combinari(nivel + 1, v + 1); // elementele cresc strict st[nivel] = 0; } }
startVal = v+1 asigurăm ordinea crescătoare și evităm duplicatele.
Generarea submulțimilor
Generăm toate submulțimile (inclusiv mulțimea vidă) ale unei mulțimi cu n elemente. Numărul total = 2ⁿ.
int n; void submultimi(int nivel) { // Afișăm submulțimea curentă (elementele cu st[i]=1) cout << "{"; bool primul = true; for (int i = 1; i <= n; i++) if (st[i] == 1) { if (!primul) cout << ","; cout << i; primul = false; } cout << "} "; if (nivel == n + 1) return; // Fiecare element poate fi inclus (1) sau exclus (0) st[nivel] = 1; submultimi(nivel + 1); st[nivel] = 0; submultimi(nivel + 1); }
Problema celor N Regine
int n, col[15]; // col[i] = coloana reginei de pe linia i bool esteValid(int linie, int coloana) { for (int i = 1; i < linie; i++) { if (col[i] == coloana) return false; // aceeași coloană if (abs(col[i] - coloana) == abs(i - linie)) return false; // diagonală } return true; } void regine(int linie) { if (linie == n + 1) { afiseazaSolutie(); return; } for (int c = 1; c <= n; c++) { if (esteValid(linie, c)) { col[linie] = c; regine(linie + 1); col[linie] = 0; // backtrack } } }
| N | Soluții distincte | Soluții fundamentale |
|---|---|---|
| 4 | 2 | 1 |
| 5 | 10 | 2 |
| 6 | 4 | 1 |
| 8 | 92 | 12 |
| 10 | 724 | 92 |
Demo animat — 4 Regine
Vizualizează cum algoritmul backtracking plasează reginele una câte una, detectează conflictele și revine pentru a încerca alte variante.
Backtracking în labirint
int lab[10][10], viz[10][10]; int n, m; // dimensiuni int dx[] = {-1, 0, 1, 0}; // sus, dreapta, jos, stânga int dy[] = { 0, 1, 0, -1}; bool labirint(int x, int y, int xf, int yf) { if (x == xf && y == yf) return true; // am ajuns! for (int d = 0; d < 4; d++) { int nx = x + dx[d], ny = y + dy[d]; if (nx>=0 && nx<n && ny>=0 && ny<m && !viz[nx][ny] && lab[nx][ny]==0) { viz[nx][ny] = 1; // marcăm ca vizitat if (labirint(nx, ny, xf, yf)) return true; viz[nx][ny] = 0; // backtrack — ștergem marcajul } } return false; }
Probleme tip BAC — backtracking
int st[20], n; void generate(int nivel) { if (nivel == n + 1) { /* afișare */ return; } for (int c = 0; c <= 9; c++) { if (nivel == 1 || st[nivel-1] != c) { // nu e identic cu precedentul st[nivel] = c; generate(nivel + 1); } } }
int st[30], n; void partitii(int nivel, int suma, int minVal) { if (suma == n) { /* afișare st[1..nivel-1] */ return; } for (int v = minVal; v <= n - suma; v++) { st[nivel] = v; partitii(nivel + 1, suma + v, v + 1); } } // Apel: partitii(1, 0, 1)
1. Funcția de validare (condiție de continuare)
2. Cazul de bază (soluție completă)
3. Pasul de backtrack (dezalegere)