This shows you the differences between two versions of the page.
|
pa:laboratoare:laborator-02 [2026/03/09 23:50] radu.nichita [Extra (studiu de caz pentru acasă)] |
pa:laboratoare:laborator-02 [2026/03/15 19:10] (current) aureliu.antonie [Gardurile lui Gigel] |
||
|---|---|---|---|
| Line 7: | Line 7: | ||
| ===== Precizări inițiale ===== | ===== Precizări inițiale ===== | ||
| <note> | <note> | ||
| - | Toate exemplele de cod se găsesc pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/demo/lab04|pa-lab::demo/lab04]]. | + | Toate exemplele de cod se găsesc pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/algorithms/lab02|pa-lab/algorithms/lab02]]. |
| - | 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. | + | 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. | + | * 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ă rugăm să compilați **DOAR** codul de pe GitHub. Pentru raportarea problemelor, contactați unul dintre maintaineri. | * Vă rugăm 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ă rugam să dați e-mail unuia dintre responsabili. | * Pentru orice problemă legată de conținutul acestei pagini, vă rugam să dați e-mail unuia dintre responsabili. | ||
| Line 23: | Line 23: | ||
| De asemenea, DP se poate folosi și pentru probleme în care nu căutam un optim, cum ar fi **problemele de numărare**. | De asemenea, DP se poate folosi și pentru probleme în care nu căutam un optim, cum ar fi **problemele de numărare**. | ||
| - | Pentru noțiunile prezentate până acum despre DP, vă rugăm să consultați pagina laboratorului 3. | + | Pentru noțiunile prezentate până acum despre DP, vă rugăm să consultați pagina [[pa:laboratoare:laborator-01|laboratorului 01]]. |
| ===== Exemple clasice ===== | ===== Exemple clasice ===== | ||
| Line 61: | Line 61: | ||
| == Enunț == | == Enunț == | ||
| - | Fie un produs matriceal $M = M_1 M_2 ... M_n$. Putem pune paranteze în mai multe moduri și vom obține același rezultat (înmulțire asociativă), dar este posibil să obținem un număr diferit de **înmulțiri scalare**. | + | Fie un produs matriceal $M = M_1 M_2 ... M_n$. Putem pune paranteze în mai multe moduri și vom obține același rezultat (înmulțirea este asociativă), dar este posibil să obținem un număr diferit de **înmulțiri scalare**. |
| Matricea $M_i$ are (prin convenție), dimensiunile $d_{i-1} d_{i}$. | Matricea $M_i$ are (prin convenție), dimensiunile $d_{i-1} d_{i}$. | ||
| Line 359: | Line 359: | ||
| * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \ MOD$ | * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \ MOD$ | ||
| * | * | ||
| - | <note> Așa cum am zis în secțiunea de [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli|sfaturi și reguli]] vrem să facem o **parționare** după un anumit **criteriu**: în cazul problemei de față, criteriul de parționare este dacă gardul se termină cu o scândură verticală sau orizontală. | + | <note> Așa cum am zis în secțiunea de [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-02?&#sfaturireguli|sfaturi și reguli]] vrem să facem o **parționare** după un anumit **criteriu**: în cazul problemei de față, criteriul de parționare este dacă gardul se termină cu o scândură verticală sau orizontală. |
| - | De asemenea, tot în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli|sfaturi și reguli]] am precizat că nu vrem **să număram un obiect** (un mod de a construi gardul) **de două ori**. Recurența noastră (dp[i] = dp[i-1] + dp[i-4]) nu ia un obiect de două ori pentru că orice soluție care vine din dp[i-4] e diferită de alta care vine din dp[i-1] pentru că diferă în cel puțin ultima scândură așezată) </note> | + | De asemenea, tot în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-02?&#sfaturireguli|sfaturi și reguli]] am precizat că nu vrem **să număram un obiect** (un mod de a construi gardul) **de două ori**. Recurența noastră (dp[i] = dp[i-1] + dp[i-4]) nu ia un obiect de două ori pentru că orice soluție care vine din dp[i-4] e diferită de alta care vine din dp[i-1] pentru că diferă în cel puțin ultima scândură așezată) </note> |
| == Implementare recurență == | == Implementare recurență == | ||
| Line 409: | Line 409: | ||
| De multe ori, este nevoie să folosim câteva tehnici pentru a obține performanța maximă cu recurența găsită. | De multe ori, este nevoie să folosim câteva tehnici pentru a obține performanța maximă cu recurența găsită. | ||
| - | În prima parte a laboratorului 3 se menționa tehnica de memoizare. În acesta, ne vom rezuma la cum putem folosi cunoștințele de lucru matriceal pentru a favoriza implementarea unor anumite tipuri de recurențe. | + | În prima parte a [[pa:laboratoare:laborator-01|laboratorului 01]] se menționa tehnica de **memoizare**. În acesta, ne vom rezuma la cum putem folosi cunoștințele de lucru matriceal pentru a favoriza implementarea unor anumite tipuri de recurențe. |
| ==== Exponențiere pe matrice pentru recurențe liniare ==== | ==== Exponențiere pe matrice pentru recurențe liniare ==== | ||
| Line 498: | Line 498: | ||
| $$S_i = S_{k}C^{i -k}$$ | $$S_i = S_{k}C^{i -k}$$ | ||
| + | Pentru a aduce un plus de viteză, vom folosi un truc numit [[https://en.wikipedia.org/wiki/Exponentiation_by_squaring|exponențiere rapidă]]. Pe scurt, vom ridica o valoare(notată $a$) la puterea n, împărțind n folosind reprezentarea sa binară. | ||
| - | În laboratorul 2 (Divide et Impera) am învățat că putem calcula $x ^ n$ în timp logaritmic. Deoarece și înmulțirea matricilor este asociativă, putem calcula $C ^ n$ in timp logaritmic. | + | De exemplu, dacă am vrea să calculăm: |
| + | $$ 5^{13} = 5^{1101_2}= 5^8 * 5^4 * 5^1 $$ | ||
| + | |||
| + | Exponentul are exact $\lfloor log_2(n) \rfloor + 1$ cifre în baza 2, deci vom avea nevoie de $O(log(n))$ înmulțiri, fiind condiționați de faptul că trebuie să cunoaștem $a^1, a^2, a^4, etc.$ Din fericire, putem calcula rapid acești factori prin ridicarea la pătrat a elementului precedent. | ||
| + | |||
| + | În final, putem implementa un algoritm iterativ pentru calcularea rezultatului $a^n$ iterând prin cifrele din baza 2 ale lui n și înmulțind cu ${a^2}^{k}$ atunci când cifra curentă este 1(pentru viteză, vom folosi operații pe biți în loc de o iterare propriu zisă prin cifre). | ||
| + | |||
| + | Mai jos putem vedea o implementare in C++: | ||
| + | <code cpp> | ||
| + | long long pow(long long a, long long n) { | ||
| + | long long res = 1; | ||
| + | while (n > 0) { | ||
| + | // daca cel mai nesemnificativ bit(LSB) din n este 1 | ||
| + | if (n & 1) { | ||
| + | res = res * a; | ||
| + | } | ||
| + | |||
| + | // calculam urmatorul factor | ||
| + | a = a * a; | ||
| + | |||
| + | // shiftam la dreapta n cu o pozitie(urmatorul bit va deveni LSB) | ||
| + | n >>= 1; | ||
| + | } | ||
| + | return res; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Acest truc poate fi folosit pentru orice tip de date pentru care înmulțirea este asociativă(inclusiv matrice). | ||
| Obținem astfel o soluție cu următoarele complexități: | Obținem astfel o soluție cu următoarele complexități: | ||
| Line 641: | Line 669: | ||
| - | ===== Exerciții ===== | + | ===== Pool probleme (pentru prezentări) ====== |
| + | ======= 1) Rectangle Cutting ======= | ||
| - | <hidden> | + | **Enunt:** |
| - | <spoiler Soluție> | + | Se dă un dreptunghi de dimensiuni ''a × b''. Se pot face tăieturi doar pe linii paralele cu laturile dreptunghiului, împărțind dreptunghiul în două dreptunghiuri mai mici. |
| - | Problema este preluată de [[https://infoarena.ro/problema/azerah|aici]]. Soluția se găsește [[https://www.infoarena.ro/onis-2015/solutii-runda-1#azerah|aici]]. | + | |
| - | </spoiler> | + | |
| - | </hidden> | + | |
| - | Expresie booleană | + | Determinati numărul minim de tăieturi necesare pentru a împărți dreptunghiul în pătrate. |
| - | Se dă o expresie booleană corectă cu n termeni. Fiecare din termeni poate fi unul din stringurile **true**, **false**, **and**, **or**, **xor**. | + | |
| - | Numărați modurile în care se pot așeza paranteze astfel încât rezultatul să fie **true**. Se respectă regulile de la logică (tabelele de adevăr pentru operațiile **and**, **or**, **xor**). | + | **Date de intrare:** |
| + | Două numere întregi ''a'' și ''b'' — dimensiunile dreptunghiului. | ||
| - | Deoarece rezultatul poate fi prea mare, se cere **restul împărțirii** lui la $1000000007$ ($10^9 + 7$). | + | **Date de ieșire:** |
| + | Se afișează un singur număr întreg — numărul minim de tăieturi necesare. | ||
| - | <note> | + | Problema se poate testa la: |
| - | În schelet vom codifica cu valori de tip char cele 5 stringuri: | + | https://cses.fi/problemset/task/1744 |
| - | * **false**: 'F' | + | |
| - | * **true**: 'T' | + | |
| - | * **and**: '&' | + | |
| - | * **or**: '|' | + | |
| - | * **xor**: '^' | + | |
| - | Funcția pe care va trebui să o implementați voi va folosi variabilele **n** (numărul de termeni) și **expr** (vectorul cu termenii expresiei). | + | ======= 2) Removal Game ======= |
| - | </note> | + | |
| + | **Enunt:** | ||
| + | Se dă un șir de ''n'' numere întregi. Doi jucători joacă alternativ. La fiecare mutare, un jucător poate elimina primul sau ultimul element din șir și adaugă valoarea acelui element la scorul său. | ||
| + | Ambii jucători joacă optim. | ||
| - | <spoiler Exemplu 1> | + | Determinati scorul maxim pe care îl poate obține primul jucător. |
| - | $n = 5$ și $expr = ['T', '&', 'F', '^', 'T']$ (expr = [** true and false xor true**]) | + | |
| - | Răspuns: $2$ | + | **Date de intrare:** |
| + | Pe prima linie se află un număr întreg ''n''. | ||
| + | Pe a doua linie se află ''n'' numere întregi reprezentând valorile din șir. | ||
| - | Explicație: Există 2 moduri corecte de a paranteza expresia astfel încât să obținem rezultatul **true** (1). | + | **Date de ieșire:** |
| - | * $ T&(F^T) $ | + | Se afișează un singur număr întreg — scorul maxim al primului jucător. |
| - | * $ (T&F)^T $ | + | |
| - | </spoiler> | + | |
| - | <spoiler Hint> | + | Problema se poate testa la: |
| - | Complexitate temporală dorită este $O(n ^ 3)$. | + | https://cses.fi/problemset/task/1097/ |
| - | + | ||
| - | Opțional, se pot defini funcții ajutătoare precum **is_operand**, **is_operator**, **evaluate**. | + | |
| - | </spoiler> | + | |
| - | <note tip> | + | ======= 3) Array Description ======= |
| - | Pentru rezolvarea celor două probleme gândiți-vă la ce scrise în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli| Sfaturi / Reguli]]. Pentru fiecare dintre cele două probleme facem o **partiționare după un anumit criteriu**. | + | |
| + | **Enunt:** | ||
| + | Se dă un vector de lungime ''n''. Fiecare element este un număr între 1 și ''m''. Unele poziții sunt deja fixate, iar altele au valoarea 0, ceea ce înseamnă că pot fi alese liber. | ||
| + | Un vector este valid dacă diferența absolută dintre două elemente consecutive este cel mult 1. | ||
| - | Pentru problema **DP or math?** partiționăm toate subșirurile după critieriul **parității sumei subșirului** (câte sunt pare/impare).\\ | + | Determinati câte vectori valizi pot fi formați respectând valorile deja fixate. |
| - | Pentru problema **expresie booleană** partiționăm **toate parantezările posibile după rezultatul lor** (câte dau true/false). | + | |
| + | **Date de intrare:** | ||
| + | Pe prima linie se află două numere întregi ''n'' și ''m''. | ||
| + | Pe a doua linie se află ''n'' numere întregi reprezentând vectorul inițial (valorile 0 indică poziții nefixate). | ||
| + | |||
| + | **Date de ieșire:** | ||
| + | Se afișează un singur număr întreg — numărul de vectori valizi modulo 10⁹+7. | ||
| + | |||
| + | Problema se poate testa la: | ||
| + | https://cses.fi/problemset/task/1746/ | ||
| + | |||
| + | ======= 4) Student Attendance Record II ======= | ||
| + | |||
| + | **Enunt:** | ||
| + | Un elev are un istoric al prezentețor la școală pe parcursul a ''n'' zile. Pentru fiecare zi, status-ul său poate fi: | ||
| + | * P (present) — prezent | ||
| + | * L (late) — întârziat | ||
| + | * A (absent) — absent | ||
| + | |||
| + | Un istoric este considerat valid dacă: | ||
| + | * conține cel mult o absență (A) | ||
| + | * nu conține mai mult de două întârzieri consecutive (L) | ||
| + | |||
| + | Determinati câte istorice de prezență valide de lungime ''n'' există. | ||
| + | |||
| + | **Date de intrare:** | ||
| + | Un număr întreg ''n'' — numărul de zile. | ||
| + | |||
| + | **Date de ieșire:** | ||
| + | Se afișează un singur număr întreg — numărul de istorice valide modulo 10⁹+7. | ||
| + | |||
| + | Problema se poate testa la: | ||
| + | https://leetcode.com/problems/student-attendance-record-ii/description/ | ||
| + | |||
| + | ======= 5) Burst Balloons ======= | ||
| + | |||
| + | **Enunt:** | ||
| + | Se dau ''n'' baloane, fiecare având un număr asociat. Când se sparge balonul ''i'', se câștigă un număr de monede egale cu produsul numerelor de pe balonul ''i'' și baloanele vecine lui în acel moment. | ||
| + | După spargere, balonul dispare, iar vecinii lui devin adiacenți. La extremități se consideră două baloane imaginare cu valoarea 1. | ||
| + | |||
| + | Determinati numărul maxim de monede pe care îl puteți obține spargând toate baloanele într-o anumită ordine. | ||
| + | |||
| + | **Date de intrare:** | ||
| + | Un vector de ''n'' numere întregi care reprezintă valorile baloanelor. | ||
| + | |||
| + | **Date de ieșire:** | ||
| + | Se afișează un singur număr întreg — numărul maxim de monede ce poate fi obținut. | ||
| + | |||
| + | Problema se poate testa la: | ||
| + | https://leetcode.com/problems/burst-balloons/description/ | ||
| - | </note> | ||
| ===== Extra (studiu de caz pentru acasă) ===== | ===== Extra (studiu de caz pentru acasă) ===== | ||
| <spoiler Por Costel si Azerath> | <spoiler Por Costel si Azerath> | ||
| Line 701: | Line 771: | ||
| <spoiler Parantezare booleană> | <spoiler Parantezare booleană> | ||
| - | Rezolvati pe geeksforgeeks problema [[https://www.geeksforgeeks.org/problems/boolean-parenthesization5610|Parantezare booleană]]. | + | Rezolvati pe geeksforgeeks problema [[https://www.geeksforgeeks.org/problems/boolean-parenthesization5610/1|Parantezare booleană]]. |
| Hint: Complexitate temporală dorită este $O(n ^ 3)$. | Hint: Complexitate temporală dorită este $O(n ^ 3)$. | ||