Pe scurt
În backtracking recursiv, un apel reprezintă un nivel al construcției. Bucla încearcă fiecare candidat, iar un apel nou continuă soluția parțială. Când apelul revine, următorul candidat este încercat. Condiția de oprire apare când soluția are lungimea cerută.
În detaliu
În varianta recursivă, un apel bt(k) corespunde nivelului al construcției. Dacă soluția parțială este completă ( a depășit numărul de niveluri), se prelucrează soluția (de obicei se afișează) și apelul se încheie. Altfel, un ciclu parcurge candidații nivelului ; pentru fiecare candidat acceptat se memorează în și se apelează bt(k + 1). Când apelul interior revine, ciclul continuă cu următorul candidat. Instrucțiunea return din cazul complet încheie doar acel apel, nu și apelurile deja active: explorarea reia buclele lor. Dacă soluția parțială folosește o stare modificabilă, de pildă un vector de marcaje „valoare folosită”, modificările făcute înainte de apel se anulează după revenire, ca starea să fie corectă pentru candidatul următor. Adâncimea recursiei este numărul de niveluri, deci stiva ocupă spațiu proporțional cu el.
Exemplul 1 — perechi din fără repetiție.
#include <iostream>
using namespace std;
int x[2];
void gen(int k) {
if (k == 2) {
cout << x[0] << x[1] << " ";
return;
}
for (int c = 1; c <= 2; ++c) {
if (k == 1 && c == x[0]) continue;
x[k] = c;
gen(k + 1);
}
}
int main() {
gen(0);
return 0;
}
Apelul gen(0) încearcă 1 pe primul nivel; gen(1) încearcă 1 (respins, pentru că este egal cu x[0]) și apoi 2, deci se afișează 12. Apoi gen(0) încearcă 2, iar gen(1) acceptă doar 1: se afișează 21. Programul afișează 12 21.
Exemplul 2 — arborele apelurilor. Pentru permutările mulțimii , cu un vector folosit care marchează valorile deja puse, apelul bt(1) produce 3 apeluri pe nivelul 2, 6 pe nivelul 3 și 6 pe nivelul 4 (frunzele, câte unul pentru fiecare permutare). Cu apelul inițial, numărul total de apeluri este . Pentru permutările lui sunt 24 de frunze și 65 de apeluri în total. Desenarea arborelui apelurilor ajută la urmărirea ordinii în care sunt încercați candidații.
void bt(int k) {
if (k > n) { /* soluție completă */ return; }
for (int c = 1; c <= n; ++c)
if (!folosit[c]) {
folosit[c] = true;
x[k] = c;
bt(k + 1);
folosit[c] = false;
}
}
Marcajul folosit[c] = false după revenire este esențial: fără el, valoarea ar rămâne blocată pentru toate ramurile următoare și programul ar genera prea puține soluții.
Condiții de continuare în apel. Verificarea candidatului se face înainte de apelul următor, nu după el: dacă un candidat este respins, nu se mai creează niciun apel pentru el. Aceasta reduce spațiul explorat și numărul de apeluri. Pentru perechi fără repetiție din există 1 apel inițial, 2 apeluri pe nivelul 1 și 2 apeluri pe nivelul 2: în total 5 apeluri, dar numai 2 soluții.
Exemplul 3 — oprire anticipată. Dacă se cere o singură soluție, apelurile se pot opri după prima: bt întoarce o valoare logică, iar ciclul se încheie imediat ce un apel interior raportează succes. Pentru toate soluțiile, execuția continuă până la epuizarea ciclurilor tuturor nivelurilor.
Urmărirea apelurilor pentru . bt(1) încearcă și marchează folosit[1]. bt(2) încearcă (respins, marcat), apoi , iar bt(3) ajunge la . bt(4) are , deci se afișează 123 și apelul se încheie. La revenire se șterg marcajele lui 3 și 2, iar bt(2) încearcă ; urmează bt(3) cu și se afișează 132. Ordinea completă este 123 132 213 231 312 321.
Costul. Arborele are frunze, iar afișarea unei soluții costă operații, deci timpul este de ordinul , iar memoria stivei este de ordinul .
Pentru admitere
Desenează arborele apelurilor și notează ordinea candidaților. La fiecare nod, condiția de validitate poate tăia o ramură. return în cazul complet încheie acel apel, nu toate apelurile deja active; explorarea reia buclele lor. Când subiectul cere numărul de apeluri, include apelul inițial și apelurile care ajung în cazul complet, dar nu și candidații respinși de condiție, pentru că aceștia nu produc apel.
Exemplu în formatul examenului. Câte apeluri ale funcției bt se execută în total (inclusiv apelul bt(1) din main) în programul de mai jos?
#include <iostream>
using namespace std;
int n = 3, x[5];
bool folosit[5];
void bt(int k) {
if (k > n) return;
for (int c = 1; c <= n; ++c)
if (!folosit[c]) {
folosit[c] = true;
x[k] = c;
bt(k + 1);
folosit[c] = false;
}
}
int main() {
bt(1);
return 0;
}
- a) 6
- b) 10
- c) 15
- d) 16
Răspunsul corect este d). Pe nivelul 1 există un apel. Fiecare apel de pe nivelul 1 generează 3 apeluri pe nivelul 2, fiecare dintre acestea generează 2 apeluri pe nivelul 3 (rămân 2 valori nefolosite), iar fiecare apel de pe nivelul 3 generează 1 apel pe nivelul 4, unde și funcția se încheie imediat. Numărul de apeluri pe niveluri este , , și , în total . Varianta a) numără doar frunzele, adică permutările.
Al doilea exemplu: condiția taie ramuri. Ce se afișează și câte apeluri se fac dacă în funcția bt de mai sus, cu , se înlocuiește condiția !folosit[c] cu c != x[k - 1] pentru , iar valorile merg doar de la 1 la 2?
Pe nivelul 1 se încearcă 1 și 2, fără restricție. Pe nivelurile 2 și 3 rămâne un singur candidat, cel diferit de valoarea anterioară. Se afișează 121 212, adică 2 soluții. Apelurile sunt : unul pe nivelul 1, două pe nivelul 2, două pe nivelul 3 și două pe nivelul 4.
Greșeli frecvente
- Greșit: după revenirea dintr-un apel, valoarea marcată ca folosită rămâne marcată.Corect: marcajul se anulează, ca valoarea să poată fi folosită în alte ramuri.
- Greșit:
returndin cazul complet oprește tot backtracking-ul.Corect: încheie numai apelul curent; apelurile active continuă. - Greșit: numărul de apeluri este egal cu numărul de soluții.Corect: apelurile includ și nivelurile intermediare.
Testează-te pe această lecție
Lecția o citești aici, fără cont. Testele pe lecții și capitole sunt în cont, cu grile originale Didaxis și explicația fiecăreia: gratuit 10 grile pe zi, fără limită cu Premium, 49 de lei pe lună pentru fiecare copil.
Înainte și după
Surse și referințe
Redactare proprie, conform programei:
- OMECI 5099/2009, anexa 5, Informatică, p. 4–5, clasa XI; plan de încadrare: OMEC 7723/2024, anexa 2, tabelele 1–25, legislatie.just.ro; filiera tehnologică: OMEC 7724/2024, anexa 4, tabelul calificării, legislatie.just.ro
- UNSTPB, Regulament admitere licență 2026, Anexa II-1.2 p. Anx II-14 și Anexa XI-3 p. Anx XI-6–7 (PDF p. 64, 120–121); admitere.pub.ro
- OMECTS 4800/2010, anexa 2, Informatică, p. 3–4; edu.ro
Documente consultate
- UNSTPB, admitere 2026, anexa XI-3, p. Anx XI-6–7
- Programa școlară de informatică, clasa a XI-a (OMECI 5099/2009, anexa 5), CNCE, p. 4–5; rocnee.eu
- Programa de bacalaureat la informatică, matematică-informatică (anexa 2 la OMECTS 4800/2010), Ministerul Educației, p. 4–5; edu.ro
- Subiectele și baremele Bacalaureatului 2026; subiecte2026.edu.ro
Desene: Redactare proprie (desen original), conform Programa școlară de informatică, clasa a XI-a (OMECI 5099/2009, anexa 5), CNCE; rocnee.eu
