This shows you the differences between two versions of the page.
pa:laboratoare:laborator-05 [2019/03/26 12:01] teodor_mihai.cotet [Referinte] |
pa:laboratoare:laborator-05 [2024/04/01 14:12] (current) radu.nichita |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 5: Backtracking ====== | + | ====== Laborator 05: Backtracking ====== |
- | Responsabili: | + | |
- | * [[radu.stochitoiu@gmail.com|Radu Stochitoiu]] | + | |
- | * [[razvan.m.chitu@gmail.com|Razvan Chitu]] | + | |
===== Obiective laborator ===== | ===== Obiective laborator ===== | ||
- | * Intelegerea notiunilor de baza despre backtracking; | + | * Întelegerea noțiunilor de bază despre backtracking; |
- | * Insusirea abilitatilor de implementare a algoritmilor bazati backtracking; | + | * Însușirea abilităților de implementare a algoritmilor bazați pe backtracking; |
- | * Rezolvarea unor probleme NP-complete în timp exponential. | + | * Rezolvarea unor probleme NP-complete în timp exponențial. |
- | ===== Precizari initiale ===== | + | ===== Precizări inițiale ===== |
<note> | <note> | ||
- | Toate exemplele de cod se gasesc in {{pa:new_pa:demo-lab05.zip}}. | + | Toate exemplele de cod se găsesc pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/demo/lab05|pa-lab::demo/lab05]]. |
- | Acestea apar incorporate si in textul laboratorului pentru a facilita parcurgerea cursiva a laboratorului. | + | |
+ | Exemplele de cod apar încorporate și în textul laboratorului pentru a facilita parcurgerea cursivă a acestuia. ATENȚIE! Varianta actualizată a acestor exemple se găsește întotdeauna pe GitHub. | ||
</note> | </note> | ||
+ | |||
+ | * Toate bucățile de cod prezentate în partea introductivă a laboratorului (înainte de exerciții) au fost testate. Cu toate acestea, este posibil ca din cauza mai multor factori (formatare, caractere invizibile puse de browser etc.) un simplu copy-paste să nu fie de ajuns pentru a compila codul. | ||
+ | * Vă rugam să compilați **DOAR** codul de pe GitHub. Pentru raportarea problemelor, contactați unul dintre maintaineri. | ||
+ | * Pentru orice problemă legată de conținutul acestei pagini, vă rugăm să dați e-mail unuia dintre responsabili. | ||
+ | |||
===== Ce este Backtracking? ===== | ===== Ce este Backtracking? ===== | ||
- | Backtracking este un algoritm care cauta **una sau mai multe solutii** pentru o problema, printr-o cautare exhaustiva, mai eficienta insa in general decat o abordare „genereaza si testeaza”, de tip „forta bruta”, deoarece un candidat partial care nu duce la o solutie este abandonat. Poate fi folosit pentru orice problema care presupune o cautare in **spatiul starilor**. | + | Backtracking este un algoritm care caută **una sau mai multe soluții** pentru o problema, printr-o căutare exhaustiva, mai eficientă însă în general decât o abordare „generează si testează”, de tip „forță brută”, deoarece un candidat parțial care nu duce la o soluție este abandonat. Poate fi folosit pentru orice problemă care presupune o căutare în **spațiul stărilor**. |
- | In general, in timp ce cautam o solutie e posibil sa dam de o infundatura in urma unei alegeri gresite sau sa gasim o solutie, dar sa dorim sa cautam in continuare alte solutii. In acel moment trebuie sa ne intoarcem pe pasii facuti (**backtrack**) si la un moment dat sa luam alta decizie. | + | În general, în timp ce cautăm o soluție e posibil să dăm de un deadend în urma unei alegeri greșite sau să găsim o soluție, dar să dorim să căutăm în continuare alte soluții. În acel moment trebuie să ne întoarcem pe pașii făcuți (**backtrack**) și la un moment dat să luăm altă decizie. |
- | Este relativ simplu din punct de vedere conceptual, dar complexitatea algoritmului este exponentiala. | + | Este relativ simplu din punct de vedere conceptual, dar complexitatea algoritmului este exponentială. |
- | ===== Importanţa – aplicaţii practice ===== | + | ===== Importanța – aplicaţii practice ===== |
- | Exista foarte multe probleme (de exemplu, probleme NP-complete sau NP-dificile) care pot fi rezolvate prin algoritmi de tip backtracking mai eficient decat prin „forta bruta” (adica generarea tuturor alternativelor si selectarea solutiilor). Atentie insa, complexitatea computationala este de cele mai multe ori exponentiala. O eficientizare se poate face prin combinarea cu tehnici de propagare a restrictiilor. Orice problema care are nevoie de parcurgerea spatiului de stari se poate rezolva cu backtracking. | + | Există foarte multe probleme (de exemplu, problemele NP-complete sau NP-dificile) care pot fi rezolvate prin algoritmi de tip backtracking mai eficient decât prin „forta bruta” (adică generarea tuturor alternativelor și selectarea soluțiilor). Atenție însă, complexitatea computațională este de cele mai multe ori **exponențială**. O eficientizare se poate face prin combinarea cu tehnici de propagare a restricțiilor. Orice problemă care are nevoie de parcurgerea spațiului de stări se poate rezolva cu backtracking. |
- | ===== Descrierea problemei si a rezolvarilor ===== | + | ===== Descrierea problemei și a rezolvărilor ===== |
- | Pornind de la strategiile clasice de parcurgere a **spatiului de stari**, algoritmii de tip backtracking practic enumera un set de candidati partiali, care, dupa completarea definitiva, pot deveni solutii potentiale ale problemei initiale. Exact ca strategiile de **parcurgere în latime/adancime** si backtracking-ul are la baza expandarea unui nod curent, iar determinarea solutiei se face într-o maniera incrementala. Prin natura sa, backtracking-ul este recursiv, iar în arborele expandat top-down se aplica operatii de tipul pruning (taiere) daca solutia partiala nu este valida. | + | Pornind de la strategiile clasice de parcurgere a **spațiului de stări**, algoritmii de tip backtracking practic enumeră un set de candidați parțiali, care, după completarea definitivă, pot deveni soluții potențiale ale problemei inițiale. Exact ca strategiile de **parcurgere în lățime/adâncime** și backtracking-ul are la bază expandarea unui nod curent, iar determinarea soluției se face într-o manieră incrementală. Prin natura sa, backtracking-ul este recursiv, iar în arborele expandat top-down se aplică operații de tipul pruning (tăiere) dacă soluția parțială nu este validă. |
===== Algoritm de baza ===== | ===== Algoritm de baza ===== | ||
Line 68: | Line 71: | ||
===== Exemple clasice ===== | ===== Exemple clasice ===== | ||
- | Ne vom ocupa in continuare de urmatoarele probleme: | + | Ne vom ocupa în continuare de următoarele probleme: |
- | * Permutari | + | * Permutări |
- | * Combinari | + | * Combinări |
* Aranjamente | * Aranjamente | ||
- | * Submultimi | + | * Submulțimi |
- | * Genererare de siruri | + | * Generare de șiruri |
* Problema damelor | * Problema damelor | ||
- | * Problema soricelului | + | * Problema șoricelului |
* Tic-Tac-Toe | * Tic-Tac-Toe | ||
* Sudoku | * Sudoku | ||
Line 81: | Line 84: | ||
<note> | <note> | ||
- | Sudoku si Ultimate Tic-Tac-Toe sunt probleme foarte grele. In general nu putem explora tot spatiul starilor pentru un input arbitrar dat. | + | Sudoku și Ultimate Tic-Tac-Toe sunt probleme foarte grele. În general nu putem explora tot spațiul stărilor pentru un input arbitrar dat. |
</note> | </note> | ||
- | ==== Permutari ==== | + | ==== Permutări ==== |
- | === Enunt === | + | === Enunț === |
- | Se da un numar N. Sa se genereze toate permutarile multimii formate din toate numerele de la 1 la N. | + | Se dă un număr N. Să se genereze toate permutările mulțimii formate din toate numerele de la 1 la N. |
=== Exemple === | === Exemple === | ||
Line 106: | Line 109: | ||
</spoiler> | </spoiler> | ||
- | === Solutii === | + | === Soluții === |
- | === Backtracking (algoritmul in cazul general) === | + | === Backtracking (algoritmul în cazul general) === |
<spoiler Implementare> | <spoiler Implementare> | ||
<code cpp> | <code cpp> | ||
- | /* deoarece numerele sunt sterse din domeniu odata ce sunt folosite, solutia generata este garantata | + | /* deoarece numerele sunt sterse din domeniu odata ce sunt folosite, soluția generata este garantata |
- | sa nu contina duplicate. Astfel, atunci cand domeniul ajunge vid, solutia este intotdeauna corecta */ | + | sa nu contina duplicate. Astfel, atunci cand domeniul ajunge vid, soluția este intotdeauna corecta */ |
bool check(std::vector<int> solution) { | bool check(std::vector<int> solution) { | ||
return true; | return true; | ||
Line 119: | Line 122: | ||
void printSolution(std::vector<int> solution) { | void printSolution(std::vector<int> solution) { | ||
- | for (int s : solution) { | + | for (auto &s : solution) { |
std::cout << s << " "; | std::cout << s << " "; | ||
} | } | ||
Line 164: | Line 167: | ||
</code> | </code> | ||
- | Apelarea initiala (din "int main") se face astfel: "back(domain, solution);", unde domain reprezinta un vector cu elementele de la 1 la N, iar solution este un vector gol. | + | Apelarea inițială (din "main") se face astfel: "back(domain, solution);", unde domain reprezintă un vector cu elementele de la 1 la N, iar solution este un vector gol. |
<note important> | <note important> | ||
- | Nu este indicata implementarea backtracking-ului astfel deoarece este foarte costisitor din punct de vedere al memoriei, deoarece cream noi domenii si solutii la fiecare pas. | + | Nu este indicată implementarea backtracking-ului astfel deoarece este foarte costisitor din punct de vedere al memoriei(se creează noi domenii și soluții la fiecare pas). |
</note> | </note> | ||
- | |||
</spoiler> | </spoiler> | ||
Line 175: | Line 177: | ||
== Complexitate == | == Complexitate == | ||
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea următoarele complexitati: |
- | * complexitate temporala : $T(n)=O(n * n!)=O(n!)$ | + | * complexitate temporala : $T(n)=O(n * n!)$ |
- | * explicatie : Complexitate generarii permutarilor, $O(n!)$, se inmulteste cu complexitatea copierii vectorilor solutie si domeniu si a stergerii elementelor din domeniu, $O(n)$ | + | * explicație : Complexitatea generarii permutarilor, $O(n!)$, se înmultește cu complexitatea copierii vectorilor soluție si domeniu si a stergerii elementelor din domeniu, $O(n)$ |
* complexitate spatiala : $S(n)=O(n^2)$ | * complexitate spatiala : $S(n)=O(n^2)$ | ||
- | * explicatie : Fiecare nivel de recursivitate are propria lui copie a solutiei si a domeniului. Sunt n nivele de recursivitate, deci complexitatea spatiala este $O(n * n) = O(n^2)$ | + | * explicație : Fiecare nivel de recursivitate are propria lui copie a soluției și a domeniului. Sunt n nivele de recursivitate, deci complexitatea spatială este $O(n * n) = O(n^2)$ |
Line 188: | Line 190: | ||
<code cpp> | <code cpp> | ||
- | /* deoarece numerele sunt sterse din domeniu odata ce sunt folosite, solutia generata este garantata sa nu contina duplicate. Astfel, atunci cand domeniul ajunge vid, solutia este intotdeauna corecta */ | + | /* deoarece numerele sunt sterse din domeniu odata ce sunt folosite, soluția generata este garantata sa nu contina duplicate. Astfel, atunci cand domeniul ajunge vid, soluția este intotdeauna corecta */ |
bool check(std::vector<int> solution) { | bool check(std::vector<int> solution) { | ||
return true; | return true; | ||
Line 252: | Line 254: | ||
== Complexitate == | == Complexitate == | ||
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea următoarele complexități: |
- | * complexitate temporala : $T(n)=O(n * n!)$ | + | * complexitate temporală : $T(n)=O(n * n!)$ |
- | * explicatie : Complexitate generarii permutarilor, $O(n!)$, se inmulteste cu complexitatea stergerii elementelor din domeniu, $O(n)$ | + | * explicație : Complexitatea generării permutărilor, $O(n!)$, se înmulțește cu complexitatea ștergerii elementelor din domeniu, $O(n)$ |
- | * complexitate spatiala : $S(n)=O(n)$ | + | * complexitate spatială : $S(n)=O(n)$ |
- | * explicatie : Spre deosebire de solutia anterioara, toate nivelele de recursivitate folosesc aceeasi solutie si acelasi domeniu. Complexitatea spatiala este astfel redusa la $O(n)$ | + | * explicație : Spre deosebire de solutia anterioară, toate nivelele de recursivitate folosesc aceeași soluție și același domeniu. Complexitatea spatială este astfel redusă la $O(n)$ |
<note> | <note> | ||
- | Abordarea aceasta este mai eficienta decat cea generala prin evitarea folosirii memoriei auxiliare. | + | Această abordare este mai eficientă decât cea generală, deoarece se evită folosirea memoriei auxiliare. |
</note> | </note> | ||
- | === Backtracking (taierea ramurilor nefolositoare) === | + | === Backtracking (tăierea ramurilor nefolositoare) === |
<spoiler Implementare> | <spoiler Implementare> | ||
Line 343: | Line 345: | ||
</code> | </code> | ||
- | Apelarea initiala (din "int main") se face astfel: "back(0, n, domain, solution, visited);", unde domain reprezinta un vector cu elementele de la 1 la N, iar solution este un vector de n elemente, 0 este pasul curent, n este pasul la care dorim sa ne oprim, iar visited este map-ul care ne permite sa tinem cont de ce elemente au fost vizitate sau nu. | + | Apelarea inițială (din "main") se face astfel: "back(0, n, domain, solution, visited);", unde domain reprezintă un vector cu elementele de la 1 la N, iar solution este un vector de n elemente, 0 este pasul curent, n este pasul la care dorim să ne oprim, iar visited este map-ul care ne permite să ținem cont de ce elemente au fost vizitate sau nu. |
</spoiler> | </spoiler> | ||
- | === Complexitate === | + | == Complexitate == |
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea următoarele complexitați: |
- | * complexitate temporala : $T(n)=O(n * n!)=O(n!)$ | + | * complexitate temporală : $T(n)=O(n * n!)=O(n!)$ |
- | * explicatie : Complexitate generarii permutarilor, O(n!), se inmulteste cu complexitatea | + | * explicație : Complexitatea generării permutărilor, O(n!), se înmulțește cu complexitatea iterării prin domeniu, $O(n)$ |
- | iterarii prin domeniu, $O(n)$ | + | * complexitate spatială : $S(n)=O(n)$ |
- | * complexitate spatiala : $S(n)=O(n)$ | + | * explicație : Toate nivelele de recursivitate folosesc aceeași soluție și același domeniu. |
- | * explicatie : Toate nivelele de recursivitate folosesc aceeasi solutie si acelasi domeniu. | + | |
<note> | <note> | ||
- | Aceasta solutie este optima si are complexitate temporala $T(n) = O(n!)$. Nu putem sa obtinem o solutie mai buna, intrucat trebuie sa generam n! permutari. | + | Această soluție este optimă și are complexitatea temporală $T(n) = O(n!)$. Nu putem să obținem o soluție mai bună, întrucât trebuie să generăm n! permutări. |
- | + | De asemenea, este optimă și din punct de vedere spatial, întrucât trebuie să avem $S(n) = O(n)$, din cauza stocării permutării generate. | |
- | De asemenea, este optima si din punct de vedere spatial, intrucat trebuie sa avem $S(n) = O(n)$, din cauza stocarii permutarii generate. | + | |
</note> | </note> | ||
- | ==== Combinari ==== | + | ==== Combinări ==== |
- | === Enunt === | + | === Enunț === |
- | Se dau numerele N si K. Sa se genereze toate combinarile multimii formate din toate numerele de la 1 la N, luate cate K. | + | Se dau numerele N si K. Să se genereze toate combinările mulțimii formate din toate numerele de la 1 la N, luate câte K. |
=== Exemple === | === Exemple === | ||
Line 378: | Line 378: | ||
N = 4, K = 2 => M = {1, 2, 3, 4} | N = 4, K = 2 => M = {1, 2, 3, 4} | ||
- | Solutie: | + | Soluție: |
* {1, 2} | * {1, 2} | ||
* {1, 3} | * {1, 3} | ||
Line 388: | Line 388: | ||
</spoiler> | </spoiler> | ||
- | === Solutii === | + | === Soluții === |
- | ===Backtracking (taierea ramurilor nefolositoare) === | + | ===Backtracking (tăierea ramurilor nefolositoare) === |
<spoiler Implementare> | <spoiler Implementare> | ||
<code cpp> | <code cpp> | ||
Line 443: | Line 443: | ||
</code> | </code> | ||
- | In aceasta solutie ne bazam pe faptul ca toate combinarile pot fi generate in | + | În această soluție ne bazăm pe faptul că toate combinările pot fi generate în |
- | ordine crescatoare, adica solutia {1, 3, 4} e echivalenta cu {4, 1, 3}. | + | ordine crescătoare, adică soluția {1, 3, 4} e echivalentă cu {4, 1, 3}. |
<note> | <note> | ||
- | Aceasta solutie este optima intrucat toate solutiile generate sunt corecte (de aceea functia check intoarce true). Deoarece problema cere obtinerea tuturor combinarilor, aceasta complexitate nu poate fi mai mica de Combinari(n, k). | + | Această soluție este optimă întrucât toate soluțiile generate sunt corecte (de aceea funcția check întoarce true). Deoarece problema cere obținerea tuturor combinărilor, aceasta complexitate nu poate fi mai mică de Combinări(n, k). |
</note> | </note> | ||
Line 456: | Line 456: | ||
=== Complexitate === | === Complexitate === | ||
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea următoarele complexități: |
- | * complexitate temporala : $T(n)=O(Combinari(n, k))$ | + | * complexitate temporală : $T(n)=O(Combinari(n, k))$ |
- | * complexitate spatiala : $S(n)=O(n+k)=O(n)$ | + | * complexitate spatială : $S(n)=O(n+k)=O(n)$ |
- | * explicatie : $k <= n$, deci $O(n+k)=O(n)$ | + | * explicație : $k <= n$, deci $O(n+k)=O(n)$ |
- | ==== Problema soricelului ==== | + | ==== Problema șoricelului ==== |
- | === Enunt === | + | === Enunț === |
- | Se da un numar N si o matrice patratica de dimensiuni N x N in care elementele | + | Se dă un număr N și o matrice pătratică de dimensiuni N x N în care elementele |
- | egale cu 1 reprezinta ziduri (locuri prin care nu se poate trece), iar cele egale | + | egale cu 1 reprezintă ziduri (locuri prin care nu se poate trece), iar cele egale |
- | cu 0 reprezinta spatii goale. Aceasta matrice are un soricel in celula (0, 0) si | + | cu 0 reprezintă spații goale. Această matrice are un șoricel în celula (0, 0) și |
- | o bucata de branza in celula (N - 1, N - 1). Scopul soricelului e sa ajunga la | + | o bucată de brânză în celula (N - 1, N - 1). Scopul șoricelului e să ajungă la |
- | bucata de branza. Afisati toate modurile in care poate face asta stiind ca | + | bucata de brânză. Afișați toate modurile în care poate face asta știind că |
- | acesta poate merge doar in dreapta sau in jos cu cate o celula la fiecare pas. | + | acesta poate merge doar în dreapta sau în jos cu câte o celulă la fiecare pas. |
=== Exemple === | === Exemple === | ||
Line 481: | Line 481: | ||
* 0 0 | * 0 0 | ||
- | Exista 1 drum posibil: | + | Există 1 drum posibil: |
* (0, 0)->(1, 0)->(1, 1) | * (0, 0)->(1, 0)->(1, 1) | ||
Line 494: | Line 494: | ||
* 0 0 0 | * 0 0 0 | ||
- | Exista 2 drumuri posibile: | + | Există 2 drumuri posibile: |
* (0,0)->(0,1)->(0,2)->(1,2)->(2,2) | * (0,0)->(0,1)->(0,2)->(1,2)->(2,2) | ||
Line 509: | Line 509: | ||
* 0 0 0 0 | * 0 0 0 0 | ||
- | Exista 4 drumuri posibile: | + | Există 4 drumuri posibile: |
* (0,0)->(1,0)->(2,0)->(2,1)->(2,2)->(2,3)->(3,3) | * (0,0)->(1,0)->(2,0)->(2,1)->(2,2)->(2,3)->(3,3) | ||
Line 518: | Line 518: | ||
</spoiler> | </spoiler> | ||
- | === Solutii === | + | === Soluții === |
- | === Backtracking (transmitere prin referinta) === | + | === Backtracking (transmitere prin referință) === |
<spoiler Implementare> | <spoiler Implementare> | ||
Line 618: | Line 618: | ||
=== Complexitate === | === Complexitate === | ||
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea următoarele complexități: |
- | * complexitate temporala : $T(n)=O(Aranjamente(n^2, 2n - 1))$ | + | * complexitate temporală : $T(n)=O(Aranjamente(n^2, 2n - 1))$ |
- | * explicatie: Initial in domeniu avem $n^2$ valori. Noi dorim sa generam toate submultimile ordonate de cate $2n-1$ elemente. Acestea sunt tocmai aranjamentele de $n^2$ luate cate $2n-1$. | + | * explicație: Initial in domeniu avem $n^2$ valori. Noi dorim sa generam toate submultimile ordonate de cate $2n-1$ elemente. Acestea sunt tocmai aranjamentele de $n^2$ luate cate $2n-1$. |
- | * complexitate spatiala : $S(n)=O(n^2)$ | + | * complexitate spatială : $S(n)=O(n^2)$ |
- | * explicatie: Trebuie sa stocam informatie despre drum, care are $2n-1$ celule; stocam domeniul care are n^2 elemente | + | * explicație: Trebuie să stocăm informație despre drum, care are $2n-1$ celule; stocăm domeniul care are $n^2$ elemente |
- | === Backtracking (taierea ramurilor nefolositoare) === | + | === Backtracking (tăierea ramurilor nefolositoare) === |
<spoiler Implementare> | <spoiler Implementare> | ||
Line 728: | Line 728: | ||
=== Complexitate === | === Complexitate === | ||
- | Solutia va avea urmatoarele complexitati: | + | Soluția va avea urmatoarele complexitati: |
- | * complexitate temporala : $T(n)=O(2^{2n})$ | + | * complexitate temporală : $T(n)=O(2^{2n})$ |
- | * explicatie: avem de urmat un sir de $2n-1$ mutari, iar la fiecare pas avem 2 variante posibile | + | * explicație: avem de urmat un șir de $2n-1$ mutari, iar la fiecare pas avem 2 variante posibile |
* complexitate spatiala : $S(n)=O(n)$ | * complexitate spatiala : $S(n)=O(n)$ | ||
- | * explicatie: stocam maximum $2n-1$ casute | + | * explicație: stocam maximum $2n-1$ căsuțe |
- | ===== Exercitii ===== | + | ===== Exerciții ===== |
<note> | <note> | ||
- | In acest laborator vom folosi scheletul de laborator din arhiva {{pa:new_pa:skel-lab05.zip}}. | + | Scheletul de laborator se găsește pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/skel/lab05|pa-lab::skel/lab05]]. |
</note> | </note> | ||
- | |||
==== Aranjamente ==== | ==== Aranjamente ==== | ||
- | Fie N si K doua **numere naturale strict pozitive**. Se cere afisarea tuturor aranjamentelor de N elemente luate cate K din multimea {1, 2, ..., N}. | + | Fie N și K două **numere naturale strict pozitive**. Se cere afișarea tuturor aranjamentelor de N elemente luate cate K din mulțimea {1, 2, ..., N}. |
<spoiler Exemplu 1> | <spoiler Exemplu 1> | ||
Line 747: | Line 746: | ||
Fie N = 3, K = 2 => M = {1, 2, 3} | Fie N = 3, K = 2 => M = {1, 2, 3} | ||
- | Solutie: | + | Soluție: |
* {1, 2} | * {1, 2} | ||
* {1, 3} | * {1, 3} | ||
Line 758: | Line 757: | ||
<note> | <note> | ||
- | Se doreste o complexitate $T(n, k) = A(n,k)$. | + | Se dorește o complexitate $T(n, k) = A(n,k)$. |
</note> | </note> | ||
<spoiler Hint> | <spoiler Hint> | ||
- | Folositi-va de problema **Permutari**. | + | Folosiți-vă de problema **Permutări**. |
</spoiler> | </spoiler> | ||
<note> | <note> | ||
- | Solutiile se vor genera in ordine lexico-grafica! | + | Soluțiile se vor genera în ordine lexico-grafica! |
- | Checkerul asteapta sa le stocati in aceasta ordine. | + | Checkerul așteaptă să le stocați în această ordine. |
</note> | </note> | ||
- | ==== Submultimi ==== | + | ==== Submulțimi ==== |
- | Fie N un **numar natural strict pozitiv**. Se cere afisarea tuturor submultimilor multimii {1, 2, ..., N}. | + | Fie N un **număr natural strict pozitiv**. Se cere afișarea tuturor submulțimilor mulțimii {1, 2, ..., N}. |
<spoiler Exemplu 1> | <spoiler Exemplu 1> | ||
Line 779: | Line 778: | ||
Fie N = 4 => M = {1, 2, 3, 4} | Fie N = 4 => M = {1, 2, 3, 4} | ||
- | Solutie: | + | Soluție: |
- | {} - multimea vida | + | {} - mulțimea vidă |
{1} | {1} | ||
{1, 2} | {1, 2} | ||
Line 801: | Line 800: | ||
<note> | <note> | ||
- | Se doreste o complexitate $T(n) = O(2^n)$. | + | Se dorește o complexitate $T(n) = O(2^n)$. |
</note> | </note> | ||
<spoiler Hint> | <spoiler Hint> | ||
- | Folositi-va de problema **Combinari**. | + | Folosiți-vă de problema **Combinari**. |
</spoiler> | </spoiler> | ||
<note> | <note> | ||
- | Solutiile se vor genera in ordine lexico-grafica! | + | Soluțiile se vor genera în ordine lexico-grafica! |
- | Checkerul asteapta sa le stocati in aceasta ordine. | + | Checkerul așteaptă să le stocați în această ordine. |
</note> | </note> | ||
==== Problema damelor ==== | ==== Problema damelor ==== | ||
- | Problema damelor (sau problema reginelor) trateaza plasarea a 8 regine de sah pe o tablă de șah de dimensiuni 8 x 8 astfel incat sa nu existe doua regine care se ameninta reciproc. Astfel, se cauta **o solutie** astfel incat nicio pereche de doua regine sa nu fie pe acelasi rand, pe aceeasi coloana, sau pe aceeasi diagonala. Problema cu opt regine este doar un caz particular pentru problema generala, care presupune plasarea a N regine pe o tablă de sah N x N în aceleasi conditii. Pentru aceasta problema, există solutii pentru toate numerele naturale N cu excepția lui N = 2 si N = 3. | + | Problema damelor (sau problema reginelor) tratează plasarea a 8 regine de sah pe o tablă de șah de dimensiuni 8 x 8 astfel încat să nu existe două regine care se amenință reciproc. Astfel, se caută **o soluție** astfel încât nicio pereche de doua regine să nu fie pe același rând, pe aceeași coloană, sau pe aceeași diagonală. Problema cu opt regine este doar un caz particular pentru problema generală, care presupune plasarea a N regine pe o tablă de șah N x N în aceleasi condiții. Pentru această problemă, există soluții pentru toate numerele naturale N cu excepția lui N = 2 si N = 3. |
<spoiler Exemplu 1> | <spoiler Exemplu 1> | ||
Line 824: | Line 823: | ||
Fie N = 5 | Fie N = 5 | ||
- | Solutie: | + | Soluție: |
|X|-|-|-|-| | |X|-|-|-|-| | ||
Line 832: | Line 831: | ||
|-|-|-|X|-| | |-|-|-|X|-| | ||
- | X reprezinta o dama, - reprezinta spatiu gol. | + | X reprezintă o damă, - reprezintă spațiu gol. |
</spoiler> | </spoiler> | ||
Line 839: | Line 838: | ||
<spoiler Hint> | <spoiler Hint> | ||
- | E nevoie sa facem backtracking pe matrice sau e suficient pe vector? | + | E nevoie să facem backtracking pe matrice sau e suficient pe vector? |
</spoiler> | </spoiler> | ||
Line 846: | Line 845: | ||
<note> | <note> | ||
- | Se va cauta o singura solutie (**oricare** solutie corecta), care va fi returnata sub format unui vector cu $n + 1$ elemente. | + | Se va caută o singură soluție (**oricare** soluție corectă), care va fi returnată sub forma unui vector cu $n + 1$ elemente. |
- | Solutia este $sol[0], sol[1], ..., sol[n]$, unde $sol[i]$ = coloana unde vom plasa regina de pe linia i. | + | Soluția este $sol[0], sol[1], ..., sol[n]$, unde $sol[i]$ = coloana unde vom plasa regina de pe linia i. |
- | Elementul 0 este nefolosit, dorim sa pastram conventia cu indexare de la 1. | + | Elementul 0 este nefolosit, dorim să păstrăm convenția cu indexare de la 1. |
</note> | </note> | ||
- | ==== Generare de siruri ==== | + | ==== Generare de șiruri ==== |
- | Vi se da o lista de caractere si o lista de frecvente (pentru caracterul de pe | + | Vi se dă o listă de caractere și o lista de frecvențe (pentru caracterul de pe |
- | pozitia i, frecventa de pe pozitia i). Vi se cere sa generati toate sirurile | + | poziția i, frecvența de pe poziția i). Vi se cere să generați toate șirurile |
- | care se pot forma cu aceste caractere si aceste frecvente stiind ca nu pot fi | + | care se pot forma cu aceste caractere și aceste frecvențe știind că nu pot fi |
- | mai mult de K aparitii consecutive ale aceluiasi caracter. | + | mai mult de K apariții consecutive ale aceluiași caracter. |
<spoiler Exemplu 1> | <spoiler Exemplu 1> | ||
Line 865: | Line 864: | ||
Fie caractere[] = {'a', 'b', 'c'}, freq[] = {1, 1, 2}, K = 5 | Fie caractere[] = {'a', 'b', 'c'}, freq[] = {1, 1, 2}, K = 5 | ||
- | Solutie: | + | Soluție: |
* abcc | * abcc | ||
* acbc | * acbc | ||
Line 890: | Line 889: | ||
* bcbbc | * bcbbc | ||
* bcbcb | * bcbcb | ||
+ | * bccbb | ||
* cbbcb | * cbbcb | ||
* cbcbb | * cbcbb | ||
Line 897: | Line 897: | ||
<note> | <note> | ||
- | Solutiile se vor genera in ordine lexico-grafica! | + | Soluțiile se vor genera în ordine lexico-grafica! |
- | Checkerul asteapta sa le stocati in aceasta ordine. | + | Checkerul așteaptă să le stocați în această ordine. |
</note> | </note> | ||
===== Bonus ===== | ===== Bonus ===== | ||
Line 905: | Line 905: | ||
==== Problema damelor (AC3) ==== | ==== Problema damelor (AC3) ==== | ||
- | **Aplicati AC3 pe problema damelor.** | + | **Aplicați AC3 pe problema damelor.** |
- | Algoritmul AC-3 (Arc Consistency Algorithm) este de obicei folosit in probleme de satisfacere a constrangerilor (CSP). Acesta sterge arcele din arborele de stari care sigur nu se vor folosi niciodata. | + | Algoritmul AC-3 (Arc Consistency Algorithm) este de obicei folosit în probleme de satisfacere a constrângerilor (CSP). Acesta șterge arcele din arborele de stări care sigur nu se vor folosi niciodata. |
- | AC-3 lucreaza cu: | + | AC-3 lucrează cu: |
- | * constrangeri | + | * constrângeri |
* variabile | * variabile | ||
* domenii de variabile | * domenii de variabile | ||
- | O variabila poate lua orice valoare din domeniul sau la orice pas. O constrangere este o relatie sau o limitare a unor variabile. | + | O variabilă poate lua orice valoare din domeniul său la orice pas. O constrângere este o relație sau o limitare a unor variabile. |
=== Exemplu AC-3 === | === Exemplu AC-3 === | ||
- | Consideram A, o variabila ce are domeniul D(A) = {0, 1, 2, 3, 4, 5, 6} si B o variabila ce are domeniul D(B) = {0, 1, 2, 3, 4}. Cunoastem constrangerile: C1 = "A trebuie sa fie impar" si C2 = "A + B trebuie sa fie egal cu 5". | + | Considerăm A, o variabilă ce are domeniul D(A) = {0, 1, 2, 3, 4, 5, 6} și B o variabila ce are domeniul D(B) = {0, 1, 2, 3, 4}. Cunoaștem constrângerile: C1 = "A trebuie să fie impar" și C2 = "A + B trebuie să fie egal cu 5". |
- | Algoritmul AC-3 va elimina in primul rand toate valorile pare ale lui A pentru a respecta C1 => D(A) = {1, 3, 5}. Apoi, va incerca sa satisfaca C2, asa ca va pastra in domeniul lui B toate valorile care adunate cu valori din D(A) pot da 5 => D(B) = {0, 2, 4}. | + | Algoritmul AC-3 va elimina în primul rând toate valorile pare ale lui A pentru a respecta C1 => D(A) = {1, 3, 5}. Apoi, va încerca să satisfacă C2, așa că va păstra în domeniul lui B toate valorile care adunate cu valori din D(A) pot da 5 => D(B) = {0, 2, 4}. |
- | AC-3 a redus astfel domeniile lui A si B, reducand semnificativ timpul folosit de algoritmul backtracking. | + | AC-3 a redus astfel domeniile lui A si B, reducând semnificativ timpul folosit de algoritmul backtracking. |
===== Extra ===== | ===== Extra ===== | ||
Line 933: | Line 933: | ||
</spoiler> | </spoiler> | ||
- | ==== Ultimate Tic Tac Toe ==== | ||
- | [[http://theaigames.com/competitions/ultimate-tic-tac-toe | The AI Games - Ultimate Tic Tac Toe]] | ||
+ | <spoiler Backtracking problems> | ||
+ | Articolul de pe [[https://leetcode.com/tag/backtracking/| leetcode]] conține o listă cu diverse tipuri de probleme de programare dinamică, din toate categoriile discutate la PA (plus multe altele). | ||
+ | </spoiler> | ||
+ | |||
+ | ===== Referințe ===== | ||
+ | [0] Chapter **Backtracking**, “Introduction to Algorithms”, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein |