Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pa:laboratoare:laborator-04 [2019/03/20 10:05]
gabriel.bercaru [Exemple clasice]
pa:laboratoare:laborator-04 [2023/03/31 11:47] (current)
radu.iacob [Exponențiere pe matrice pentru recurențe liniare]
Line 1: Line 1:
-====== Laborator 4: Programare Dinamică (continuare) ====== +====== Laborator 04: Programare Dinamică (2/2) ======
-Responsabili:​ +
-  * [[visanr95@gmail.com|Radu Vișan]] +
-  * [[neatudarius@gmail.com|Darius Neațu]] +
-  * [[cristb@gmail.com|Cristian Banu]] +
-  * [[razvan.ch95@gmail.com|Răzvan Chițu]] +
  
 ===== Obiective laborator ===== ===== Obiective laborator =====
-  * Ințelegerea noțiunilor de bază despre ​programare ​dinamică +  * Înțelegerea noțiunilor de bază despre ​programarea ​dinamică. 
-  * Insușirea abilităților de implementare a algoritmilor bazați ​programare ​dinamică.+  * Însușirea abilităților de implementare a algoritmilor bazați ​pe programarea ​dinamică.
  
-===== Precizari initiale ​=====+===== Precizări inițiale ​=====
 <​note>​ <​note>​
-Toate exemplele de cod se gasesc in {{pa:new_pa:demo-lab04.zip}}.+Toate exemplele de cod se găsesc pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​demo/​lab04|pa-lab::demo/lab04]].
  
-Acestea ​apar incorporate si in textul laboratorului pentru a facilita parcurgerea ​cursiva ​laboratorului.+Exemplele de cod apar încorporate și în textul laboratorului pentru a facilita parcurgerea ​cursivă ​acestuia. **ATENȚIE!** Varianta actualizată a acestor exemple se găsește întotdeauna pe GitHub.
 </​note>​ </​note>​
  
-  * Toate bucatile ​de cod prezentate ​in partea ​introductiva ​a laboratorului (inainte ​de exercitii) 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 ​sa 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 cadin cauza mai multor factori (formatare, caractere invizibile puse de browseretc.)un simplu copy-paste ​să nu fie de ajuns pentru a compila codul. 
-  * Va rugam sa incercati si codul din arhiva ​** demo-lab04.zip**, inainte ​de a raporta ca ceva nu merge:D +  * Vă rugăm să compilați ​**DOAR** codul de pe GitHub. Pentru raportarea problemelor,​ contactați unul dintre maintaineri.  
-  * Pentru orice problema legata ​de continutul ​acestei pagini, ​va rugam sa dati email unuia dintre responsabili.+  * Pentru orice problemă legată ​de conținutul ​acestei pagini, ​vă rugam să dați e-mail ​unuia dintre responsabili.
  
 ===== Ce este DP? ===== ===== Ce este DP? =====
-Similar cu greedy, tehnica de programare ​dinamica ​este folosită pentru rezolvarea **problemelor de optimizare**.  +Similar cu greedy, tehnica de programare ​dinamică ​este folosită pentru rezolvarea **problemelor de optimizare**.  
-In continuare vom folosi acronimul **DP (dynamic programming)**.+În continuare vom folosi acronimul **DP (dynamic programming)**.
  
  
-De asemenea, DP se poate folosi ​si pentru probleme ​in care nu cautam ​un optim, cum ar fi **problemele de numarare**.+De asemenea, DP se poate folosi ​și pentru probleme ​în care nu căutam ​un optim, cum ar fi **problemele de numărare**.
  
-Pentru ​restul notiunilor ​prezentate ​pana acum despre DP, va rugam sa consulati ​pagina laboratorului 3.+Pentru ​noțiunile ​prezentate ​până ​acum despre DP, vă rugăm să consultați ​pagina laboratorului 3.
 ===== Exemple clasice ===== ===== Exemple clasice =====
  
-Programarea Dinamică este cea mai flexibilă ​tehnica din programare. Cel mai ușor mod de a o înțelege presupune parcurgerea cât mai multor exemple.+Programarea Dinamică este cea mai flexibilă ​tehnică a programării. Cel mai ușor mod de a o înțelege presupune parcurgerea cât mai multor exemple.
  
-Propunem ​cateva ​categorii de recurente, ​pe care le vom grupa astfel: +Propunem ​câteva ​categorii de recurențe ​pe care le vom grupa astfel: 
-  *  ​recurente ​de tip **SSM** (Subsecventa ​de Suma Maxima+  *  ​recurențe ​de tip **SSM** (Subsecvență ​de Sumă Maximă
-  *  ​recurente ​de tip **RUCSAC** +  *  ​recurențe ​de tip **RUCSAC** 
-  *  ​recurente ​de tip **PODM** (Parantezare ​Optima ​de Matrici+  *  ​recurențe ​de tip **PODM** (Parantezare ​Optimă ​de Matrice
-  *  ​recurente ​de tip **numărat** +  *  ​recurențe ​de tip **numărat** 
-  *  ​recurente ​pe **grafuri**+  *  ​recurențe ​pe **grafuri**
  
  
 <​note>​ <​note>​
-Pentru o problema data, este **posibil** ​sa gasim **mai multe recurente ​corecte **(mai multe solutii ​posibile). Evident, criteriul de alegere ​intre acestea va fi cel bazat pe complexitate.+Pentru o problemă dată, este **posibil** ​să găsim ​**mai multe recurențe ​corecte** (mai multe soluții ​posibile). Evident, criteriul de alegere ​între ​acestea va fi cel bazat pe complexitate.
 </​note>​ </​note>​
 ===== Categoria 3: PODM ===== ===== Categoria 3: PODM =====
-Aceste ​recurente ​au o oarecare ​asemanare ​cu problema PODM (enunt solutie).+Aceste ​recurențe ​au o oarecare ​asemănare ​cu problema PODM (enunț ​soluție).
  
  
-ATENTIE! Acest tip de recurente ​poate fi mai greu (decat celelalte). ​Puteti ​consulta ** acasa **materialele puse la dispozitie ​pentru ​intelege ​mai bine aceasta ​categorie.+**ATENȚIE!** Acest tip de recurențe ​poate fi mai greu (decât ​celelalte). ​Puteți ​consulta **acasă** materialele puse la dispoziție ​pentru ​a înțelege ​mai bine această ​categorie.
  
 Caracteristici:​ Caracteristici:​
-  ​* Acest tip de problema ​presupune ​ca o putem formula ca pe o problema ​de tip ** subinterval $[i, j]$**.  + 
-  * Daca dorim sa gasim optimul pentru acest interval, va trebuie sa luam in calcul toate combinatiile ​de 2 subproblemele ​care ar fi putut genera ​solutie ​pentru ​probleme ​**$[i, j]$**. +  ​* Acest tip de problemă ​presupune ​că o putem formula ca pe o problemă ​de tip **subinterval $[i, j]$**.  
-  * Se considera ​fiecare divizare ​in 2 subprobleme, ​data de intermediarul k +  * Dacă dorim să găsim ​optimul pentru acest interval, va trebui să luăm în calcul toate combinațiile ​de 2 subprobleme ​care ar putea genera ​o soluție ​pentru ​problemele ​**$[i, j]$**. 
-    * **$[i, k]$** si **$[k + 1, j]$ ** sunt cele 2 subprobleme pentru care cunoastem solutiile +  * Se consideră ​fiecare divizare ​în 2 subprobleme, ​dată de intermediarul k, astfel: 
-    * atunci o solutie ​pentru **$[i,j]$** se poate obtine imbinandu-le pe cele doua +    * Fie **$[i, k]$** și **$[k + 1, j]$ ** cele 2 subprobleme pentru care cunoaștem soluțiile, ​atunci o soluție ​pentru **$[i,j]$** se poate obține îmbinându-le pe cele două 
-    * ca sa gasim solutia ​cea mai buna+    * pentru a gasi soluția ​cea mai bună:
       * vom itera prin toate valorile k posibile       * vom itera prin toate valorile k posibile
-      * vom alege pe cea care maximizeaza solutia ​problemei **$[i,​j]$** +      * vom alege pe cea care maximizează soluția ​problemei **$[i,​j]$** 
-  * Calculul se face de la intervale mici (probleme ​usoare ​- **$[i,i]$** sau **$[i, i+1]$**) spre probleme generale (dimensiune ​generala ​- ** $[i, j]$ **). In final se ajunge ​si la dimensiunile ​initiale ​(**$[1, n]$**). +  * Calculul se face de la intervale mici (probleme ​ușoare ​- **$[i,i]$** sau **$[i, i+1]$**) spre probleme generale (dimensiune ​generală ​- ** $[i, j]$ **). În finalse ajunge ​și la dimensiunile ​inițiale ​(**$[1, n]$**). 
-    * Privind imaginea de ansamblu, ​adica cum se completeaza ​matricea, ​obervam ca matricea dp se completeaza ​**diagonala ​cu diagonala**.+    * Privind imaginea de ansamblu, ​adică modul în care se completează ​matricea ​dpobservăm că aceasta ​se completează ​**diagonală ​cu diagonală**.
  
 ==== Exemple clasice ==== ==== Exemple clasice ====
 === PODM === === PODM ===
  
-== Enunt == +== Enunț ​== 
-Fie un produs ​matricial ​$M = M_1 M_2 ... M_n$. Putem pune paranteze ​in mai multe moduri ​si vom obtine acelasi ​rezultat (inmultire asociativa), dar este posibil ​sa obtinem numar diferit de ** inmultiri ​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țire asociativă), dar este posibil ​să obținem un număr ​diferit de **înmulțiri ​scalare**.
  
-Matricea $M_i$ are (prin conventie), dimensiunile $d_{i-1} d_{i}$.+Matricea $M_i$ are (prin convenție), dimensiunile $d_{i-1} d_{i}$.
  
-== Cerinta ​== +== Cerință ​== 
-Se cere sa se gaseasca ​o ** parantezare ​optima ​de matrice ** (PODM), ​adica sa se gaseasca ​o parantezare care sa minimizeze ​numarul ​de inmultiri ​scalare.+Se cere să se găsească ​o **parantezare ​optimă ​de matrice ** (PODM), ​adică să se găsească ​o parantezare care să minimizeze ​numărul ​de înmulțiri ​scalare.
  
 == Exemple == == Exemple ==
Line 81: Line 75:
 |d|2|3|4|5|| |d|2|3|4|5||
  
-Raspuns: ** 64 **  (inmultiri ​scalare)+Răspuns: ** 64 **  (înmulțiri ​scalare)
  
-Explicatie: Avem 3 matrici:+Explicație: Avem 3 matrice:
    * A de dimensiuni (2, 3)    * A de dimensiuni (2, 3)
    * B de (3, 4)    * B de (3, 4)
    * C de (4, 5)    * C de (4, 5)
  
-In functie ​de ordinea ​efectuarii inmultirilor matriciale ​numarul ​total de inmultiri ​scalare poate sa fie foarte diferit: +În funcție ​de ordinea ​efectuării înmulțirilor matricealenumărul ​total de înmulțiri ​scalare poate să fie foarte diferit: 
-   * $(AB)C$ => $24 + 40 = 64$ de inmultiri +   * $(AB)C$ => $24 + 40 = 64$ de înmulțiri 
-       ​* ​explicatie: $X = (AB)$ genereaza ​$2 * 3 * 4 = 24$ inmultiri, $(XC)$ ​genereaza ​$2 * 4 * 5 = 40$ de inmultiri +       ​* ​explicație: $X = (AB)$ generează ​$2 * 3 * 4 = 24$ înmulțiri, $(XC)$ ​generează ​$2 * 4 * 5 = 40$ de înmulțiri 
-   * $A(BC)$ =>  $60 + 30 = 90$ de inmultiri +   * $A(BC)$ =>  $60 + 30 = 90$ de înmulțiri 
-       ​* ​explicatie: $X =(BC)$ ​genereaza ​$3 * 4 * 5 = 60$ inmultiri, $(AX)$ ​genereaza ​$2 * 3 * 5 = 30$ de inmultiri+       ​* ​explicație: $X =(BC)$ ​generează ​$3 * 4 * 5 = 60$ înmulțiri, $(AX)$ ​generează ​$2 * 3 * 5 = 30$ de înmulțiri
        
-Rezultatul optim se obtine ​pentru ​cea de a treia parantezare:​ $(AB)C$.+Rezultatul optim se obține ​pentru ​prima parantezare:​ $(AB)C$.
    
 </​spoiler>​ </​spoiler>​
Line 104: Line 98:
 |d|2|3|4|2|3| |d|2|3|4|2|3|
  
-Raspuns: ** 48 **  (inmultiri ​scalare)+Răspuns: ** 48 **  (înmulțiri ​scalare)
  
-Explicatie: Avem 4 matrici:+Explicație: Avem 4 matrice:
    * A de dimensiuni (2, 3)    * A de dimensiuni (2, 3)
    * B de (3, 4)    * B de (3, 4)
Line 112: Line 106:
    * D de (2, 3)    * D de (2, 3)
        
-   In functie ​de ordinea ​efectuarii inmultirilor matricialenumarul ​total de inmultiri ​scalare poate sa fie foarte diferit: +   În funcție ​de ordinea ​efectuării înmulțirilor matricealenumărul ​total de înmulțiri ​scalare poate să fie foarte diferit: 
-   * $(AB)C)D$ ​ => $24 + 16 + 12 = 52$ inmultiri +   * $(AB)C)D$ ​ => $24 + 16 + 12 = 52$ înmulțiri 
-       ​* ​explicatie: $X = (AB)$ genereaza ​$2 * 3 *4 = 24$ inmultiri ​scalare, $Y = (XC)$ genereaza ​$2 * 4 * 2 = 16$ inmultiri ​scalare, $Z = YD$ genereaza ​$2 * 2 *3 = 12$ inmultiri ​scalare  +       ​* ​explicație: $X = (AB)$ generează ​$2 * 3 *4 = 24$ înmulțiri ​scalare, $Y = (XC)$ generează ​$2 * 4 * 2 = 16$ înmulțiri ​scalare, $Z = YD$ generează ​$2 * 2 *3 = 12$ înmulțiri ​scalare  
-   * $(A(BC))D$ => $24 + 12 + 12 = 48$ inmultiri +   * $(A(BC))D$ => $24 + 12 + 12 = 48$ înmulțiri 
-       ​* ​explicatie: $X = (BC)$ genereaza ​$3 * 4 * 2 = 24$ inmultiri ​scalare, $Y = (AX)$ genereaza ​$ 2 * 3 * 2= 12$ inmultiri ​scalare, $Z = YD$ genereaza ​2 * 2 * 3$ = 12$ inmultiri ​scalare +       ​* ​explicație: $X = (BC)$ generează ​$3 * 4 * 2 = 24$ înmulțiri ​scalare, $Y = (AX)$ generează ​$ 2 * 3 * 2= 12$ înmulțiri ​scalare, $Z = YD$ generează ​2 * 2 * 3$ = 12$ înmulțiri ​scalare 
-   * $(AB)(CD)$ => $ = $ inmultiri +   * $(AB)(CD)$ => $ = $ inmulțiri 
-       ​* ​explicatie: $X = (AB)$ genereaza ​$2 * 3 * 4 = 24$ inmultiri ​scalare, $Y = (CD)$ genereaza ​$4 * 2 * 3 = $24 inmultiri ​scalare, $Z = XY$ genereaza ​$2 * 4 * 3 = 24$ inmultiri ​scalare +       ​* ​explicație: $X = (AB)$ generează ​$2 * 3 * 4 = 24$ înmulțiri ​scalare, $Y = (CD)$ generează ​$4 * 2 * 3 = 24$ înmulțiri ​scalare, $Z = XY$ generează ​$2 * 4 * 3 = 24$ înmulțiri ​scalare 
-   * $A((BC)D)$ => $24 + 18 + 27 = 69$ inmultiri +   * $A((BC)D)$ => $24 + 18 + 27 = 69$ înmulțiri 
-       ​* ​explicatie: $X = (BC)$ genereaza ​$3  * 4 * 2 = 24$ inmultiri ​scalare, $Y = (XD)$ genereaza ​$3 * 2 * 3 = 18$ inmultiri ​scalare, $Z = AY$ genereaza ​$3 * 3 * 3 = 27$ inmultiri ​scalare +       ​* ​explicație: $X = (BC)$ generează ​$3  * 4 * 2 = 24$ înmulțiri ​scalare, $Y = (XD)$ generează ​$3 * 2 * 3 = 18$ înmulțiri ​scalare, $Z = AY$ generează ​$3 * 3 * 3 = 27$ înmulțiri ​scalare 
-   * $A(B(CD))$ => $24 + 36 + 18 = 78$ inmultiri +   * $A(B(CD))$ => $24 + 36 + 18 = 78$ înmulțiri 
-       ​* ​explicatie: $X = (CD)$ genereaza ​$4 * 2 * 3 = 24$ inmultiri ​scalare, $Y = (BX)$ genereaza ​$3 * 4 * 3 =36 $ inmultiri ​scalare, $Z = AY$ genereaza ​$2 * 3 * 3 = 18$ inmultiri ​scalare +       ​* ​explicație: $X = (CD)$ generează ​$4 * 2 * 3 = 24$ înmulțiri ​scalare, $Y = (BX)$ generează ​$3 * 4 * 3 = 36$ înmulțiri ​scalare, $Z = AY$ generează ​$2 * 3 * 3 = 18$ înmulțiri ​scalare 
-   ​Rezultatul optim se obtine ​pentru cea de a treia parantezare:​ $((A(BC))D)$.+   ​Rezultatul optim se obține ​pentru cea de a treia parantezare:​ $((A(BC))D)$.
    
 </​spoiler>​ </​spoiler>​
Line 133: Line 127:
 |d|13|5|89|3|34| |d|13|5|89|3|34|
  
-Raspuns: ** 2856 **  (inmultiri ​scalare)+Răspuns: ** 2856 **  (înmulțiri ​scalare)
  
-Explicatie: Avem 4 matrici:+Explicație: Avem 4 matrice:
    * A de dimensiuni (13, 5)    * A de dimensiuni (13, 5)
    * B de (5, 89)    * B de (5, 89)
Line 141: Line 135:
    * D de (3, 34)    * D de (3, 34)
        
-   In functie ​de ordinea ​efectuarii inmultirilor ​matriciale , numarul ​total de inmultiri ​scalare poate sa fie foarte diferit: +   În funcție ​de ordinea ​efectuării înmulțirilor ​matriciale, ​numărul ​total de înmulțiri ​scalare poate să fie foarte diferit: 
-   * $((AB)C)D$ => 10582 inmultiri +   * $((AB)C)D$ => 10582 înmulțiri 
-   * $(AB)(CD)$ => 54201 inmultiri +   * $(AB)(CD)$ => 54201 înmulțiri 
-   * $(A(BC))D$ =>  2856 inmultiri +   * $(A(BC))D$ =>  2856 înmulțiri 
-   * $A((BC)D)$ =>  4055 inmultiri+   * $A((BC)D)$ =>  4055 înmulțiri
    * ...    * ...
-Rezultatul optim se obtine ​pentru cea de a treia parantezare:​ $(A(BC))D$.+ 
 +Rezultatul optim se obține ​pentru cea de a treia parantezare:​ $(A(BC))D$.
    
 </​spoiler>​ </​spoiler>​
  
 == TIPAR == == TIPAR ==
-A fost descris ​in detaliu mai sus (cand s-a vorbit de categorie).+A fost descris ​în detaliu mai sus (când s-a vorbit de categorie).
    
-== Numire ​recurenta ​=== +== Numire ​recurență ​=== 
-$dp[i][j]$ = **numarul ​minim de inmultiri ​scalare** cu care se poate obtine ​produsul $M_i * M_{i+1} * ... *{M_j}$ ​+$dp[i][j]$ = **numărul ​minim de înmulțiri ​scalare** cu care se poate obține ​produsul $M_i * M_{i+1} * ... *{M_j}$ ​ 
 + 
 +Răspunsul la problemă este **dp[1][n]** .
  
-== Gasire recurenta ​== +== Găsire recurență ​== 
-  * **Cazul de baza** : +  * **Cazul de bază** : 
     * $dp[i][i] = 0 $     * $dp[i][i] = 0 $
-      * NU avem nici un efort daca nu avem ce inmulti.+      * NU avem niciun ​efort dacă nu avem ce înmulți.
     * $dp[i][i+1] = d_{i-1} d_{i} d_{i+1}$     * $dp[i][i+1] = d_{i-1} d_{i} d_{i+1}$
-      * Daca avem doua matrice, putem doar sa le inmultim. Nu are sens sa folosim paranteze. +      * Dacă avem două matrice, putem doar să le înmulțim. Nu are sens să folosim paranteze. 
-      * Daca inmultim ​2 matrice de dimensiuni $d_{i-1} * d_{i}$ ​si $d_{i} * d_{i + 1}$, avem costul $d_{i-1} d_{i} d_{i+1}$+      * Daca înmulțim ​2 matrice de dimensiuni $d_{i-1} * d_{i}$ ​și $d_{i} * d_{i + 1}$, avem costul $d_{i-1} d_{i} d_{i+1}$
   * **Cazul general**: $dp[i][j] = min(dp[i][k] + dp[k+1][j] + d_{i-1} d_{k} d_{j})$, unde $k = i : j - 1$   * **Cazul general**: $dp[i][j] = min(dp[i][k] + dp[k+1][j] + d_{i-1} d_{k} d_{j})$, unde $k = i : j - 1$
-    * daca avem de efectuat ​sirul de inmultiri ​$M_i ... M_j$, atunci putem pune paranteze oriunde ​si sa facem inmultirile ​astfel $(M_i ... M_k) (M_{k+1} ... M_{j})$+    * dacă avem de efectuat ​șirul ​de înmulțiri ​$M_i ... M_j$, atunci putem pune paranteze oriunde ​și să facem înmulțirile ​astfel $(M_i ... M_k) (M_{k+1} ... M_{j})$
        * costul minim pentru $(M_i ... M_k)$ este $dp[i][k]$        * costul minim pentru $(M_i ... M_k)$ este $dp[i][k]$
        * costul minim pentru $(M_{k+1} ... M_j)$ este $dp[k + 1][j]$        * costul minim pentru $(M_{k+1} ... M_j)$ este $dp[k + 1][j]$
-       * vom avea in final de inmultit ​2 matrice de dimensiune $d_{i-1} * d_{k}$ si $d_{k} * d_{j}$, ​operatie ​care are costul $d_{i-1}d_{k}d_{j}$ +       * vom avea, în finalde înmulțit ​2 matrice de dimensiune $d_{i-1} * d_{k}$ si $d_{k} * d_{j}$, ​operație ​care are costul $d_{i-1}d_{k}d_{j}$ 
-     ​* ​insumam ​cele 3 costuri intermediare+     ​* ​însumăm ​cele 3 costuri intermediare
  
 == Implementare == == Implementare ==
-Puteti ​rezolva ​si testa problema PODM pe infoarena [[https://​infoarena.ro/​problema/​podm|aici]].+Puteți ​rezolva ​și testa problema PODM pe infoarena [[https://​infoarena.ro/​problema/​podm|aici]].
  
-Un exemplu de implementare ​in C++ se gaseste ​mai jos.+Un exemplu de implementare ​în C++ se găsește ​mai jos.
 <spoiler Implementare C++> <spoiler Implementare C++>
 <code cpp> <code cpp>
-// kInf este valoarea ​maxima ​- "​infinitul"​ nostru +// INF este valoarea ​maximă ​- "​infinitul"​ nostru 
-const unsigned long long kInf = std::​numeric_limits<​unsigned long long>::​max();​+const auto INF = std::​numeric_limits<​unsigned long long>::​max();​
  
 // T = O(n ^ 3)  // T = O(n ^ 3) 
-// S = O(n ^ 2) - stocam ​n x n intregi in tabloul dp+// S = O(n ^ 2) - stocăm ​n x n întregi în tabloul dp
  ​unsigned long long solve_podm(int n, const vector<​int>​ &d) {  ​unsigned long long solve_podm(int n, const vector<​int>​ &d) {
-    // dp[i][j] = numarul ​MINIM inmultiri ​scalare cu codare poate fi calculat produsul +    // dp[i][j] = numărul ​MINIM înmulțiri ​scalare cu codarepoate fi calculat produsul 
-    //            ​matricial ​M_i * M_i+1 * ... * M_j +    //            ​matriceal ​M_i * M_i+1 * ... * M_j 
-    vector<​vector<​unsigned long long>> ​ dp(n + 1, vector<​unsigned long long> (n + 1, kInf));+    vector<​vector<​unsigned long long>> ​ dp(n + 1, vector<​unsigned long long> (n + 1, INF));
  
-    // Cazul de baza 1: nu am ce inmulti ​+    // Cazul de bază 1: nu am ce înmulți ​
     for (int i = 1; i <= n; ++i) {     for (int i = 1; i <= n; ++i) {
-        dp[i][i] = 0ULL;  // 0 pe unsigned long long (voi folosi mai incolo si 1ULL)+        dp[i][i] = 0ULL;  // 0 pe unsigned long long (voi folosi mai încolo și 1ULL)
     }     }
  
-    // Cazul de baza 2: matrice d[i - 1] x d[i] inmultia ​cu matrice d[i] x d[i + 1]  +    // Cazul de bază 2: matrice d[i - 1] x d[i] înmulțită ​cu matrice d[i] x d[i + 1]  
-    // (matrice pe pozitii ​consecutive)+    // (matrice pe poziții ​consecutive)
     for (int i = 1; i < n; ++i) {     for (int i = 1; i < n; ++i) {
         dp[i][i + 1] =  1ULL * d[i - 1] * d[i] * d[i + 1];  ​         dp[i][i + 1] =  1ULL * d[i - 1] * d[i] * d[i + 1];  ​
Line 200: Line 197:
     // Cazul general:     // Cazul general:
     // dp[i][j] = min(dp[i][k] + dp[k + 1][j] + d[i - 1] * d[k] * d[j]), k = i : j - 1     // dp[i][j] = min(dp[i][k] + dp[k + 1][j] + d[i - 1] * d[k] * d[j]), k = i : j - 1
-    for (int len = 2; len <= n; ++len) {            // fixam lungimea intervalului (2, 3, 4, ...) +    for (int len = 2; len <= n; ++len) {            // fixăm ​lungimea intervalului (2, 3, 4, ...) 
-        for (int i = 1; i + len - 1 <= n; ++i) {    // fixam capatul ​din stanga: i +        for (int i = 1; i + len - 1 <= n; ++i) {    // fixăm capătul ​din stânga: i 
-            int j = i + len - 1;                    // capatul ​din dreapta se deduce: j+            int j = i + len - 1;                    // capătul ​din dreapta se deduce: j
             ​             ​
-            // Iteram ​prin indicii dintre capete, ​spargand sirul de inmultiri ​in doua (paranteze).+            // Iterăm ​prin indicii dintre capete, ​spărgând șirul ​de înmulțiri ​in două (paranteze).
             for (int k = i; k < j; ++k) {             for (int k = i; k < j; ++k) {
                 // M_i * ... M_j  = (M_i * .. * M_k) * (M_k+1 *... * M_j)                 // M_i * ... M_j  = (M_i * .. * M_k) * (M_k+1 *... * M_j)
                 unsigned long long new_sol = dp[i][k] + dp[k + 1][j] + 1ULL * d[i - 1] * d[k] * d[j];                 unsigned long long new_sol = dp[i][k] + dp[k + 1][j] + 1ULL * d[i - 1] * d[k] * d[j];
                 ​                 ​
-                // actualizam solutia daca este mai buna+                // actualizăm soluția dacă este mai bună
                 dp[i][j] = min(dp[i][j],​ new_sol); ​                 dp[i][j] = min(dp[i][j],​ new_sol); ​
             }             }
Line 215: Line 212:
     }     }
  
-    // Rezultatul se afla in dp[1][n]: ​Numarul ​MINIM de inmultiri scalare +    // Rezultatul se află în dp[1][n]: ​Numărul ​MINIM de inmultiri scalare 
-    // pe care trebuie ​sa le facem pentru a obtine ​produsul M_1 * ... * M_n+    // pe care trebuie ​să le facem pentru a obține ​produsul M_1 * ... * M_n
     return dp[1][n];     return dp[1][n];
  
Line 222: Line 219:
 </​code>​ </​code>​
 <​note>​ <​note>​
-Sursa a fost scrisa ​pentru a fi testata ​pe infoarena. ​In cazul problemei [[https://​infoarena.ro/​problema/​podm | PODM]], deoarece avem o suma de foarte multe produse, rezultatul este foarte mare. Pe infoarena se cerea ca rezultatul ​sa fie afisat ​asa cum e, garantandu-se ca incape ​pe 64 biti.+Sursa a fost scrisă ​pentru a fi testată ​pe infoarena. ​În cazul problemei [[https://​infoarena.ro/​problema/​podm | PODM]], deoarece avem o sumă de foarte multe produse, rezultatul este foarte mare. Pe infoarena se cerea ca rezultatul ​să fie afișat ​asa cum e, garantându-se că încape ​pe 64 biți.
 </​note>​ </​note>​
  
Line 228: Line 225:
 </​spoiler>​ </​spoiler>​
 <​note>​ <​note>​
-Reamintim ​ca prin inmultirea/adunarea a doua variabile de tipul **int**, rezultatul poate sa nu incapa ​pe 32 biti. De aceea, ​in solutia prezentata, s-a facut cast pe 64biti.+Reamintim ​că prin înmulțirea/adunarea a două variabile de tipul **int**, rezultatul poate să nu încapă ​pe 32 biți. De aceea, ​în soluția prezentată, s-a făcut ​cast pe 64 biți.
 </​note>​ </​note>​
  
  
 <​note>​ <​note>​
-ATENTIE! La PA, in general, vom folosi ​conventia ​$ expresie \ \%  \ kMod $, care va fi detaliata in capitolul ​urmatorul ​din acest laborator. ​+**ATENȚIE!** La PA, în general, vom folosi ​convenția ​$ expresie \ \%  \ MOD $, care va fi detaliată în capitolul ​următor ​din acest laborator. ​
 </​note>​ </​note>​
  
 == Complexitate == == Complexitate ==
-Intrucat solutia ​presupune fixarea capetelor unui subinterval (i, j), apoi alegerea unui intermediar (k), complexitatea este data de aceste 3 cicluri. +Întrucat soluția ​presupune fixarea capetelor unui subinterval (i, j), apoi alegerea unui intermediar (k), complexitatea este dată de aceste 3 cicluri. 
-    * **complexitate ​temporala**: $T(n) = O(n^3)$ +    * **complexitate ​temporală**: $T(n) = O(n^3)$ 
-    * **complexitate ​spatiala**: $S(n) = O(n^2)$ ​+    * **complexitate ​spațială**: $S(n) = O(n^2)$ ​
  
  
  
-===== Categoria 4: NUMARAT ​===== +===== Categoria 4: NUMĂRAT ​===== 
-Aceste ​recurente ​au o oarecare ​asemanare+Aceste ​recurențe ​au o oarecare ​asemănare
-  * toate numara ​lucruri! :p +  * toate numară ​lucruri! :p 
-  * interesante sunt cazurile ​cand numarul cautat ​este foarte mare (altfel am putea apela la alte metode - ex. generarea tuturor ​candidatilor ​posibili cu backtracking) +  * interesante sunt cazurile ​când numărul căutat ​este foarte mare (altfel am putea apela la alte metode - ex. generarea tuturor ​candidaților ​posibili cu backtracking) 
-      * in acest caz, deoarece ​numarul ​poate sa nu incapa ​pe un tip reprezentabil standard (ex. int pe 32/64 de biti), se cere (de obicei) restul ​impartirii numarului cautat ​la un numar **MOD** (vom folosi ​in continuare ​aceasta notatie).+      * în acest caz, deoarece ​numărul ​poate să nu încapă ​pe un tip reprezentabil standard (ex. int pe 32/64 de biți), se cere (de obicei) restul ​împarțirii numărului căutat ​la un număr ​**MOD** (vom folosi ​în continuare ​această notație).
 ==== Sfaturi / Reguli ==== ==== Sfaturi / Reguli ====
-  * cand cautati ​recurenta ​pentru o problema de numarare ​trebuie ​sa aveti grija la doua aspecte: +  * când căutați ​recurență ​pentru o problema de numărare ​trebuie ​să aveți grijă ​la două aspecte: 
-    * 1) sa **NU** ​numarati acelasi ​obiect de doua ori. +    * 1) să **NU** ​numărați același ​obiect de două ori. 
-    * 2) sa numarati ​toate obiectele ​in cauza.  +    * 2) să numărați ​toate obiectele ​în cauză.  
-  * de multe ori o problema ​de numarare implica ​partitionare ​a **tuturor** posibilelor ​solutii dupa un anumit criteriu (relevant). ​Gasirea ​criteriului este partea ​esentiala ​pentru ​gasirea recurentei+  * de multe oriproblemă ​de numărare implică ​partiționare ​a **tuturor** posibilelor ​soluții după un anumit criteriu (relevant). ​Găsirea ​criteriului este partea ​esențială ​pentru ​aflarea recurenței
 ==== Regulile de lucru cu clase de resturi ==== ==== Regulile de lucru cu clase de resturi ====
-Reamintim ​cateva proprietati ​matematice pe care ar trebui ​sa le aveti in vedere atunci ​in implementati ​pentru ​obtine ​corect resturile ​pentru anumite ​expresii. (corect poate sa insemne, de exemplu, ​sa evitati ​overflow :D - lucru neintuitiv ​cateodata). +Reamintim ​câteva proprietăți ​matematice pe care ar trebui ​să le aveți în vedere atunci ​când implementați ​pentru ​a obține ​corect resturile ​anumitor ​expresii. (corect poate să însemne, de exemplu, ​să evitați ​overflow :D - lucru neintuitiv ​câteodată). 
-  * proprietati ​de de baza+  * proprietăți ​de bază:
     * $(a + b)   \ \% \   MOD = ((a   \ \% \   MOD) + (b   \ \% \   ​MOD)) ​  \ \% \   MOD $      * $(a + b)   \ \% \   MOD = ((a   \ \% \   MOD) + (b   \ \% \   ​MOD)) ​  \ \% \   MOD $ 
     * $(a \ * b)   \ \% \   MOD = ((a   \ \% \   MOD) \ * (b   \ \% \   ​MOD)) ​  \ \% \   MOD $      * $(a \ * b)   \ \% \   MOD = ((a   \ \% \   MOD) \ * (b   \ \% \   ​MOD)) ​  \ \% \   MOD $ 
-    * $(a - b)   \ \% \   MOD = ((a   \ \% \   MOD) - (b   \ \% \   MOD) + MOD)   \ \% \   MOD $ (restul nu poate fi ceva negativ; ​in C++ **%** nu functioneaza ​pe numere negative) ​+    * $(a - b)   \ \% \   MOD = ((a   \ \% \   MOD) - (b   \ \% \   MOD) + MOD)   \ \% \   MOD $ (restul nu poate fi ceva negativ; ​în C++ **%** nu funcționează ​pe numere negative) ​
   * invers modular   * invers modular
     * $ \frac{a}{b} ​  \ \% \   MOD = ((a   \ \% \   MOD) * (b ^ {MOD-2} ​  \ \% \   ​MOD)) ​  \ \% \   MOD)$     * $ \frac{a}{b} ​  \ \% \   MOD = ((a   \ \% \   MOD) * (b ^ {MOD-2} ​  \ \% \   ​MOD)) ​  \ \% \   MOD)$
-         ​* ​ **DACA** MOD este prim; **DACA** a si b nu sunt multipli ai lui MOD   +         ​* ​ **DACĂ** MOD este prim; **DACĂ** a și b nu sunt multipli ai lui MOD   
-<​spoiler ​Explicatii ​invers modular> ​    +<​spoiler ​Explicații ​invers modular> ​    
-    * ** definitie ​**: **b** este inversul modular ​lui **a** in raport cu **MOD** ​daca $ a * b = 1 (modulo \ MOD)$+    * ** definiție ​**: **b** este inversul modular ​al lui **a** în raport cu **MOD** ​dacă $ a * b = 1 (modulo \ MOD)$
     * ** utilizare **: $ \frac{a}{b} ​  \ \% \   MOD = ((a   \ \% \   MOD) * (invers(b) ​  \ \% \   ​MOD)) ​  \ \% \   MOD $     * ** utilizare **: $ \frac{a}{b} ​  \ \% \   MOD = ((a   \ \% \   MOD) * (invers(b) ​  \ \% \   ​MOD)) ​  \ \% \   MOD $
-    * ** calculare **: deoarece la PA aceasta discutie ​are sens doar in contextul ​posibilitatii implementarii ​unei recurente ​DP in care folosim resturile doar pentru a evita overflow/​imposibilitatea de a retine ​rezultatul pe tipurile standard de tip int (adica nu ne intereseaza sa dam o metoda ​generala ​pentru invers modular), vom simplifica problema - **MOD este prim!!!** +    * ** calculare **: deoarece la PA această discuție ​are sens doar în contextul ​posibilității implementării ​unei recurențe ​DP în care folosim resturile doar pentru a evita overflow/​imposibilitatea de a reține ​rezultatul pe tipurile standard de tip int (adică ​nu ne interesează să dăm o metoda ​generală ​pentru invers modular), vom simplifica problema - **MOD este prim!!!** 
-      * ** Mica teorema ​a lui Fermat**: ​Daca p este un numar prim si a este un număr ​intreg ​care nu este multiplu al lui p, atunci $a^{p-1} = 1 (modulo \ p)$. +      * ** Mica teoremă ​a lui Fermat**: ​Dacă p este un număr ​prim și a este un număr ​întreg ​care nu este multiplu al lui p, atunci $a^{p-1} = 1 (modulo \ p)$. 
-            * din definitia ​inversului modular, reiese ​ca ** a ** si **b** nu sunt multipli ai lui **MOD** +            * din definiția ​inversului modular, reiese ​că ** a ** și **b** nu sunt multipli ai lui **MOD** 
-            * introducand notatiile ​noastre ​in teorema si prelucrand obtinem+            * introducând notațiile ​noastre ​în teoremă și prelucrând obținem
               * $a ^ {MOD - 1} = 1 (modulo \ MOD) <=> a * (a ^{MOD-2}) = 1 (modulo \ MOD)$               * $a ^ {MOD - 1} = 1 (modulo \ MOD) <=> a * (a ^{MOD-2}) = 1 (modulo \ MOD)$
-              * deci inversul modular al lui a (in aceste ​conditii ​specifice) este $b = a ^ {MOD -2 }$+              * deciinversul modular al lui a (în aceste ​condiții ​specifice) este $b = a ^ {MOD -2 }$
  
  
Line 276: Line 273:
  
 <​note>​ <​note>​
-Reamintim ​ca prin inmultirea/adunarea a doua variabile de tipul int, rezultatul poate sa nu incapa ​pe 32 biti. E posibil ​sa trebuiasca sa combinam ​regulile ​cu resturi cu urmatoarele:+Reamintim ​că prin înmulțirea/adunarea a două variabile de tipul int, rezultatul poate să nu încapă ​pe 32 biți. E posibil ​să trebuiască să combinăm ​regulile ​de la resturi cu următoarele:
   * C++   * C++
-    * **1LL / 1ULL** - constanta 1 pe 64 biti cu semn / fara semn +    * **1LL / 1ULL** - constanta 1 pe 64 biti cu semn / făra semn 
-      * **1LL * a * b** - am grija ca rezultatul ​sa nu dea overflow ​si sa se stocheze direct pe 64biti ​(cu semn)+      * **1LL * a * b** - am grijă ​ca rezultatul ​să nu dea overflow ​și să se stocheze direct pe 64 biți (cu semn)
   * Java   * Java
-    * **1L** - constanta 1 pe 64biti ​cu semn (in Java nu exista ​unsigned types) +    * **1L** - constanta 1 pe 64 biți cu semn (în Java nu există ​unsigned types) 
-      * **1L * a * b** - am grija ca rezultatul ​sa nu dea overflow ​si sa se stocheze direct pe 64biti ​(cu semn)+      * **1L * a * b** - am grijă ​ca rezultatul ​să nu dea overflow ​și să se stocheze direct pe 64 biți (cu semn)
 </​note>​ </​note>​
 ==== Gardurile lui Gigel  ==== ==== Gardurile lui Gigel  ====
-=== Enunt === +=== Enunț ​=== 
-Gigel trece de la furat obiecte cu un rucsac la numarat ​garduri (fiecare are micile lui placeri ​:D). El doreste sa construiasca ​un gard folosind ​in mod repetat **un singur tip de piesa**.+Gigel trece de la furat obiecte cu un rucsac la numărat ​garduri (fiecare are micile lui plăceri ​:D). El dorește să construiască ​un gard folosind ​în mod repetat **un singur tip de piesă**.
  
-piesa are dimensiunile ** 4 x 1 ** (o unitate = 1m). Din motive irelevante pentru ​aceasta ​problema, orice gard construit trebuie ​sa aiba **inaltime ​4m** in orice punct.+piesă ​are dimensiunile ** 4 x 1 ** (o unitate = 1m). Din motive irelevante pentru ​această ​problema, orice gard construit trebuie ​să aibă **înălțimea ​4m** în orice punct.
  
-piesa poate fi pusa in pozitie ​**orizontala** sau in pozitie ​** verticala**. +piesă ​poate fi pusă în poziție ​**orizontală** sau în poziție ​**verticală**. 
  
-=== Cerinta ​=== +=== Cerință ​=== 
-Gigel se intreaba ​**cate garduri de lungime n** si inaltime 4 exista? Deoarece ​celalalt ​prenume al lui este Bulănel, el intuieste ca acest numar este foarte mare, de aceea va cere ** restul ​impartirii** acestui numar la **1009**.+Gigel se întreabă ​**câte garduri de lungime n și înălțime 4** există? Deoarece ​celălalt ​prenume al lui este Bulănel, el intuiește că acest număr ​este foarte mare, de aceea va cere ** restul ​împărțirii** acestui numar la **1009**.
  
 <spoiler Exemplu 123> <spoiler Exemplu 123>
Line 301: Line 298:
  
  
-Raspuns: ** 1 **  (un singur gard)+Răspuns: **1**  (un singur gard)
  
-Explicatie: Se poate forma un singur gard in fiecare caz, dupa cum este ilustrat ​si in figura **Garduri_123**. ​+Explicație: Se poate forma un singur gard în fiecare caz, după cum este ilustrat ​și în figura **Garduri_123**. ​
 </​spoiler>​ </​spoiler>​
  
Line 312: Line 309:
 $n = 4$ $n = 4$
  
-Raspuns: ** 2 **  ​+Răspuns: **2**  ​
  
-Explicatie: Se pot forma 2 garduri, ​in functie ​de cum asezam ​piesele, ​dupa cum este ilustrat ​si in figura **Garduri_4**.  +Explicație: Se pot forma 2 garduri, ​în funcție ​de cum așezăm ​piesele, ​după cum este ilustrat ​și în figura **Garduri_4**.  
-Observam ca de fiecare ​daca cand punem o piesa in pozitie orizontala, de fapt suntem ​obligati sa punem 4 piese, una peste alta!+Observăm că de fiecare ​dată când punem o piesă în poziție orizontală, de fapt suntem ​obligați să punem 4 piese, una peste alta!
 </​spoiler>​ </​spoiler>​
  
Line 324: Line 321:
 $n = 5$ $n = 5$
  
-Raspuns: ** 3 **  ​+Răspuns: **3**  ​
  
-Explicatie: Se pot forma 3 garduri, ​in functie ​de cum asezam ​piesele, ​dupa cum este ilustrat ​si in figura **Garduri_5**. +Explicație: Se pot forma 3 garduri, ​în funcție ​de cum așezăm ​piesele, ​după cum este ilustrat ​și în figura **Garduri_5**. 
-  * daca dorim ca acest gard sa se termine cu 4 piese in pozitie ​**orizontala** (una peste alta - marcat cu rosu), atunci la stanga ​mai ramane de completat **un subgard de lungime 1**, in toate modurile posibile +  * dacă dorim ca acest gard să se termine cu 4 piese în poziție ​**orizontală** (una peste alta - marcat cu roșu), atunci la stânga ​mai ramane de completat **un subgard de lungime 1**, în toate modurile posibile 
-  * daca dorim ca acest gard sa se termine cu o piesa in pozitie ​**verticala** (marcat cu rosu), atunci la stanga ​mai ramane ​de completat **un subgard de lungime 4**, in toate modurile posibile+  * dacă dorim ca acest gard să se termine cu o piesă în poziție ​**verticală** (marcat cu roșu), atunci la stânga ​mai rămâne ​de completat **un subgard de lungime 4**, în toate modurile posibile
 </​spoiler>​ </​spoiler>​
  
Line 337: Line 334:
 $n = 6$ $n = 6$
  
-Raspuns: ** 4 **  ​+Răspuns: **4**  ​
  
-Explicatie: Se pot forma 4 garduri, ​in functie ​de cum asezam ​piesele, ​dupa cum este ilustrat ​si in figura **Garduri_6**. +Explicație: Se pot forma 4 garduri, ​în funcție ​de cum așezăm ​piesele, ​după cum este ilustrat ​și în figura **Garduri_6**. 
-  * daca dorim ca acest gard sa se termine cu o piesa in pozitie ​**verticala** (marcat cu rosu), atunci la stanga ​mai ramane ​de completat **un subgard de lungime 5**, in toate modurile posibile +  * dacă dorim ca acest gard să se termine cu o piesă în poziție ​**verticală** (marcat cu roșu), atunci la stânga ​mai rămâne ​de completat **un subgard de lungime 5**, în toate modurile posibile 
-  * daca dorim ca acest gard sa se termine cu 4 piese in pozitie ​**orizontala** (una peste alta - marcat cu rosu), atunci la stanga ​mai ramane de completat **un subgard de lungime 2**, in toate modurile posibile+  * dacă dorim ca acest gard să se termine cu 4 piese în poziție ​**orizontală** (una peste alta - marcat cu roșu), atunci la stânga ​mai ramane de completat **un subgard de lungime 2**, în toate modurile posibile
 </​spoiler>​ </​spoiler>​
  
-=== Recurenta ​=== +=== Recurență ​=== 
-== Numire ​recurenta ​== +== Numire ​recurență ​== 
-$dp[i] $ = numarul ​de garduri de lungime i si inaltime ​4 (nimic special - exact ceeea ce se cere in enunt)+$dp[i] $ = numărul ​de garduri de lungime i și înălțime ​4 (nimic special - exact ceea ce se cere în enunț)
  
  
-Raspunsul ​la problema ​este $dp[n]$.+Răspunsul ​la problemă ​este $dp[n]$.
  
-== Gasire recurenta ​== +== Găsire recurență ​== 
-  * **Caz de baza**+  * **Caz de bază**
     * $dp[1] = dp[2] = dp[3] = 1$; $dp[4]$ = 2     * $dp[1] = dp[2] = dp[3] = 1$; $dp[4]$ = 2
   * ** Caz general **   * ** Caz general **
-    * atunci dorim sa formam ​un gard de lungime i ($ i >= 5 $) am vazut ca putem alege cum sa punem ultima/​ultimele piese +    * atunci ​când dorim să formăm ​un gard de lungime i ($i >= 5$) am văzut că putem alege cum să punem ultima/​ultimele piese 
-      * **DACA** alegem ca ultima ​piesa sa fie pusa in pozitie verticala, atunci la stanga ​mai ramane ​de completat **un subgard de lungime $i-1$**  +      * **DACĂ** alegem ca ultima ​piesă să fie pusă în poziție verticală, atunci la stânga ​mai rămâne ​de completat **un subgard de lungime $i-1$**  
-        * numarul ​de moduri ​in care putem face acest subgard este $dp[i-1]$ +        * numărul ​de moduri ​în care putem face acest subgard este $dp[i-1]$ 
-      * **DACA** alegem ca ultima ​piesa sa fie in pozitie orizontala ​(de fapt punem 4 piese in pozitie orizontala), atunci la stanga ​mai ramane ​de completat **un subgard de lungime $i-4$** +      * **DACĂ** alegem ca ultima ​piesă să fie în poziție orizontală ​(de faptpunem 4 piese în poziție orizontală), atunci la stânga ​mai rămâne ​de completat **un subgard de lungime $i-4$** 
-        * numarul ​de moduri ​in care putem face acest subgard este $dp[i-4]$+        * numărul ​de moduri ​în care putem face acest subgard este $dp[i-4]$
     * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \  MOD$      * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \  MOD$ 
     *      * 
-<​note> ​Asa cum am zis in sectiunea ​de [[http://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​laborator-04?&#​sfaturireguli|sfaturi ​si reguli]] vrem sa facem o **partionare** dupa un anumit **criteriu**, in cazul problemei de fata criteriul de partionare ​este daca gardul se termina ​cu o scandura verticala ​sau orizontala.+<​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ă.
  
  
-De asemenea tot in sectiunea ​[[http://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​laborator-04?&#​sfaturireguli|sfaturi ​si reguli]] am precizat ​ca nu vrem **sa numaram ​un obiect** (un mod de a construi gardul)** de doua ori**. ​Recurenta noastra ​(dp[i] = dp[i-1] + dp[i-4]) nu ia un obiect de doua ori pentru ​ca orice solutie ​care vine din dp[i-4] e diferita ​de alta care vine din dp[i-1] pentru ​ca difera in cel putin ultima ​scandura asezata) </​note> ​+De asemeneatot î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> ​
  
-== Implementare ​recurenta ​== +== Implementare ​recurență ​== 
-Aici puteti ​vedea un exemplu simplu de implementare ​in C++. +Aici puteți ​vedea un exemplu simplu de implementare ​în C++. 
  
-<spoiler Implementare ​in C++>+<spoiler Implementare ​în C++>
 <code cpp> <code cpp>
  
 #define MOD 1009 #define MOD 1009
 int gardurile_lui_Gigel(int n) { int gardurile_lui_Gigel(int n) {
-    // cazurile de baza+    // cazurile de bază
     if (n <= 3) return 1;     if (n <= 3) return 1;
     if (n == 4) return 2;     if (n == 4) return 2;
  
-    vector<​int>​ dp(n + 1); // pastrez ​indexarea de la 1 ca in explicatii+    vector<​int>​ dp(n + 1); // păstrez ​indexarea de la 1 ca în explicații
  
-    // cazurile de baza+    // cazurile de bază
     dp[1] = dp[2] = dp[3] = 1;     dp[1] = dp[2] = dp[3] = 1;
     dp[4] = 2;     dp[4] = 2;
Line 395: Line 392:
 </​code>​ </​code>​
  
-Mentionez ca am folosit expresia ​ $dp[i] = (dp[i - 1] + dp[i - 4]) \ \%  \ MOD$ in loc de $dp[i] = ((dp[i - 1]  \ \%  \ MOD) + (dp[i - 4]  \ \%  \ MOD)) \ \%  \ MOD$, deoarece ​[e valorile anterior ​calcule in dp a fost deja aplicata operatia ​$%$.+Menționez că am folosit expresia ​ $dp[i] = (dp[i - 1] + dp[i - 4]) \ \%  \ MOD$ în loc de $dp[i] = ((dp[i - 1]  \ \%  \ MOD) + (dp[i - 4]  \ \%  \ MOD)) \ \%  \ MOD$, deoarece, pe valorile anterior ​calculate în dpa fost deja aplicată operația ​$%$.
  
-Am plecat cu numerele $1, 1, 1, 2$ si la fiecare pas rezultatul stocat este $\ \% \ MOD$, deci tot ce este stocat **deja** ​in dp este un rest in raport cu MOD. NU mai era nevoie deci sa aplica ​**%** si pe termenii din paranteza.+Am plecat cu numerele $1, 1, 1, 2$ și, la fiecare pasrezultatul stocat este $\ \% \ MOD$, decitot ce este stocat **deja** ​în dp este un rest în raport cu MOD. NU mai era nevoiedeci, să aplicăm ​**%** și pe termenii din paranteză.
 </​spoiler>​ </​spoiler>​
  
  
 == Complexitate == == Complexitate ==
-  * **complexitate ​temporala**: $T = O(n)$ +  * **complexitate ​temporală**: $T = O(n)$ 
-    * explicatie: avem o singura ​parcurgere ​in care construim tabloul dp +    * explicație: avem o singură ​parcurgere ​în care construim tabloul dp 
-    * se poate obtine ​$T=O(log n)$ folosind ​exponentiere ​pe matrice! +    * se poate obține ​$T=O(log n)$ folosind ​exponențiere ​pe matrice! 
-  * **complexitate ​spatiala**: $S = O(n)$  +  * **complexitate ​spațială**: $S = O(n)$  
-    * explicatiestocam ​tabloul dp +    * explicațiestocăm ​tabloul dp 
-    * se poate ontine ​$S = O(1)$ folosind ​exponentiere ​pe matrice! ​+    * se poate obține ​$S = O(1)$ folosind ​exponențiere ​pe matrice! ​
  
-===== Tehnici folosite ​in DP ===== +===== Tehnici folosite ​în DP ===== 
-De multe ori este nevoie ​sa folosim ​cateva ​tehnici pentru a obtine performanta maxima ​cu recurenta gasita.+De multe orieste nevoie ​să folosim ​câteva ​tehnici pentru a obține performanța maximă ​cu recurența găsită.
  
-In laboratorul ​3 se mentiona ​tehnica de memoizare ​(in prima parte a laboratorului)In acesta ne vom rezuma la cum putem folosi ​cunostintele ​de lucru matricial ​pentru a favoriza implementarea unor anumite tipuri de recurente.+În prima parte a laboratorului ​3 se menționa ​tehnica de memoizare. ​În acestane vom rezuma la cum putem folosi ​cunoștințele ​de lucru matriceal ​pentru a favoriza implementarea unor anumite tipuri de recurențe.
  
-==== Exponentiere ​pe matrice pentru ​recurente ​liniare ==== +==== Exponențiere ​pe matrice pentru ​recurențe ​liniare ==== 
-=== Recurente ​liniare=== +=== Recurențe ​liniare === 
-recurenta liniara in contextul laboratorului de DP este de forma:+recurență liniară, în contextul laboratorului de DPeste de forma:
   * $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$   * $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$
-    * pentru **KMAX o constanta** +    * pentru **KMAX o constantă** 
-    * de obicei, KMAX este foarte ​mica comparativ cu dimensinea ​n a problemei+    * de obicei, KMAX este foarte ​mică comparativ cu dimensiunea ​n a problemei
     * $c_k$ constante reale (unele pot fi nule)     * $c_k$ constante reale (unele pot fi nule)
  
-O astfel de recurenta ​ar insemna ca pentru a calcula ** costul problemei i **, imbinam ​costurile problemelor $i - 1, i-2, ...., i-k$, fiecare contribuind cu un anumit coeficient $c_{1}, c_{2}, ..., c_{k}$.+O astfel de recurență ​ar însemna că, pentru a calcula **costul problemei i**, îmbinăm ​costurile problemelor $i - 1, i - 2, ...., i - k$, fiecare contribuind cu un anumit coeficient $c_{1}, c_{2}, ..., c_{k}$.
  
-<spoiler Complexitate ​recurente liniara>  +<spoiler Complexitate ​recurențe liniară>  
-Presupunand ca nu mai exista ​alte specificatii ​ale problemei ​si ca avand cele KMAX cazuri de baza (primele KMAX valori ar trebui ​stiute/deduse prin alte reguli), atunci un algoritm poate implementa ​recurenta ​de mai sus folosind 2 cicluri de tip for (for i = 1 : n, for k = 1 : KMAX ...). +Presupunând că nu mai există ​alte specificații ​ale problemei ​și că, având ​cele KMAX cazuri de bază, ​(primele KMAX valori ar trebui ​știute/deduse prin alte reguli), atunci un algoritm poate implementa ​recurența ​de mai sus folosind 2 cicluri de tipfor (for i = 1 : n, for k = 1 : KMAX ...). 
-  * **complexitatea ​temporala** : $ T =O(n * KMAX) = O(n) $ +  * **complexitatea ​temporală** : $T = O(n * KMAX) = O(n)$ 
-    * reamintim ​ca acea valoarea ​KMAX este o constanta ​foarte ​mica in compartie ​cu n (ex. KMAX < 100) +    * reamintim ​că acea valoare ​KMAX este o constantă ​foarte ​mică în comparație ​cu n (ex. KMAX < 100) 
-  * **complexitatea ​spatiala** : $ S = O(n) $+  * **complexitatea ​spațială** : $S = O(n)$
  
-    * am presupus ​ca avem nevoie ​sa retinem ​doar tabloul dp  ​+    * am presupus ​că avem nevoie ​să reținem ​doar tabloul dp  ​
 </​spoiler>​ </​spoiler>​
  
-=== Exponentiere ​pe matrice === +=== Exponențiere ​pe matrice === 
-Facem urmatoarele notatii:+Facem următoarele notații:
   * $S_i$ = starea la pasul i   * $S_i$ = starea la pasul i
     * $S_i = (dp[i - k + 1], dp[i - k + 2], ..., dp[i - 1], dp[i])$     * $S_i = (dp[i - k + 1], dp[i - k + 2], ..., dp[i - 1], dp[i])$
-  * $S_k$ = starea ​initiala ​(in care cunoaste ​cele k cazuri de baza)+  * $S_k$ = starea ​inițială ​(în care cunoaște ​cele k cazuri de bază)
     * $S_k = (dp[1], dp[2], ..., dp[k-1], dp[k])$     * $S_k = (dp[1], dp[2], ..., dp[k-1], dp[k])$
-  * $C$ = matrice ​ce coeficienti constanti+  * $C$ = matrice ​de coeficienți constanți
     * are dimensiune $KMAX * KMAX$      * are dimensiune $KMAX * KMAX$ 
-    * putem pune constante ​in clar +    * putem pune constante ​în clar 
-    * putem pune constantele $c_k$ care tin de problema ​curenta+    * putem pune constantele $c_k$ care țin de problema ​curentă
  
  
Line 448: Line 445:
 == Algoritm naiv == == Algoritm naiv ==
 Putem formula problema astfel: Putem formula problema astfel:
-  * $S_k$ = este starea ​initiala +  * $S_k$ = este starea ​inițială 
-  * pentru a obtine ​starea ​urmatoareaplicam ​algoritmul ​urmator+  * pentru a obține ​starea ​următoareaplicăm ​algoritmul ​următor
      * $S_i = S_{i-1}C$  ​      * $S_i = S_{i-1}C$  ​
   ​   ​
 == Determinare C == == Determinare C ==
-Pentru a determina elementele matricei C, trebuie ​sa ne uitam la inmultirea matriceala ​de mai sus si sa alegem elementele lui C astfel ​incat prin inmultirealui ​$S_{i-1}$ cu $C$ sa obtinem ​elementele din $S_i$.+Pentru a determina elementele matricei C, trebuie ​să ne uităm ​la înmulțirea matriceală ​de mai sus și să alegem elementele lui C astfel ​încât ​prin înmulțirea lui $S_{i-1}$ cu $C$ să obținem ​elementele din $S_i$.
  
 \begin{gather} \begin{gather}
Line 472: Line 469:
  
  
-<​spoiler ​Explicatie ​determinare C> +<​spoiler ​Explicație ​determinare C> 
-  * ultima ​coloana contine toti coeficientii ​$c_k$ intrucat ​$dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ +  * ultima ​coloană conține toți coeficienții ​$c_k$ întrucât ​$dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ 
-  * celelalte coloane ​contin ​doar cate o valoare ​nenula +  * celelalte coloane ​conțin ​doar câte o valoare ​nenulă 
-    * pe coloana j  vom avea valoarea 1 pe linia $j+1$ ($j = 1 : KMAX - 1$) +    * pe coloana j vom avea valoarea 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) 
-      * cum obtinem, de exemplu, $dp[i - 1]$? +      * cum obținem, de exemplu, $dp[i - 1]$? 
-      * pai avem $dp[i-1]$ chiar si in starea $S_{i-1}$, deci trebuie ​sa il copiam in starea $S_i$  +      * păi, avem $dp[i-1]$ chiar și în starea $S_{i-1}$, deci trebuie ​să îl copiem în starea $S_i$  
-        * copierea se realizeaza ​prin inmultirea ​cu 1 +        * copierea se realizează ​prin inmulțirea ​cu 1 
-        * daca $dp[i-1]$ era pe ultima ​pozitiei ​(pozitia ​k) in starea $S_{i-1}$, ​in noua stare $S_i$ este pe penultima ​pozitie ​(pozitia ​$k-1$) +        * dacă $dp[i-1]$ era pe ultima ​poziție ​(poziția ​k) în starea $S_{i-1}$, ​în noua stare $S_i$ este pe penultima ​poziție ​(poziția ​$k-1$) 
-          * deci s-a deplasat la stanga ​cu o pozitie!  +          * deci s-a deplasat la stânga ​cu o poziție!  
-    * in noua stare, noua pozitie ​este deplasata ​cu o unitate la stanga fata de starea ​precedenta +    * în noua stare, noua poziție ​este deplasată ​cu o unitate la stânga față ​de starea ​precedentă 
-      * de aceea pe coloana $j$, vrem sa avem elementul 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) +      * de aceeape coloana $j$, vrem să avem elementul 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) 
-      * cand inmultim ​$S_{i-1}$ cu coloana $C_j$ **dorim ​sa** +      * când înmulțim ​$S_{i-1}$ cu coloana $C_j$ **dorim ​** 
-        * ce copiam+        * ce copiem
-          * valoarea $dp[i - KMAX + j]$ din $S_{i-1}$ ​in $S_{i}$ +          * valoarea $dp[i - KMAX + j]$ din $S_{i-1}$ ​în $S_{i}$ 
-          * adica sa copiam ​a j-a valoare de pe linie  +          * adică să copiem ​a j-a valoare de pe linie  
-        * unde copiam+        * unde copiem
-          * de pe pozitia ​$j + 1$ pe pozitia ​$j$  ​+          * de pe poziția ​$j + 1$ pe poziția ​$j$  ​
 </​spoiler>​ </​spoiler>​
  
-== Exponentiere logaritmica ​pe matrice == +== Exponențiere logaritmică ​pe matrice == 
-Algoritmul naiv de mai sus are dezavantajul ​ca are tot o complexitate ​temporala ​$O(n)$.+Algoritmul naiv de mai sus are dezavantajul ​că are tot o complexitate ​temporală ​$O(n)$.
  
-Sa executam cativa pasi de inductie ​pentru a vedea cum este determinat $S_i$.+Să executăm câțiva pași de inducție ​pentru a vedea cum este determinat $S_i$.
 $$S_i = S_{i-1}C$$ $$S_i = S_{i-1}C$$
 $$S_i = S_{i-2}C^2$$ $$S_i = S_{i-2}C^2$$
Line 502: Line 499:
  
  
-In laboratorul 2 (Divide et Impera) am invatat ca putem calcula $ x ^ n $ in timp logaritmic. Deoarece ​si inmultirea ​matricilor este asociativa, putem calcula $C ^ n$ in timp logaritmic.+Î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.
  
-Obtinem ​astfel o solutie ​cu urmatoarele complexitati+Obținem ​astfel o soluție ​cu următoarele complexități
-  * ** complexitate ​temporala ​**: $T = O(KMAX^3 * log(n))$ +  * ** complexitate ​temporală ​**: $T = O(KMAX^3 * log(n))$ 
-    * explicatie ​ +    * explicație ​ 
-      * facem doar $O(log n)$ pasi, dar un pas implica inmultire ​de matrice +      * facem doar $O(log n)$ pași, dar un pas implică înmulțire ​de matrice 
-      * o inmultire ​de matrice ​patratica ​de dimensiune KMAX are $KMAX^3$ ​operatii +      * o înmulțire ​de matrice ​patrătică ​de dimensiune KMAX are $KMAX^3$ ​operații 
-    * aceasta metoda ​este eficienta cand $KMAX << n$ (KMAX este mult mai mic decat n) +    * această metodă ​este eficientă când $KMAX << n$ (KMAX este mult mai mic decât ​n) 
-  * ** complexitatea ​spatiala ​**: $S = O(KMAX^3)$  +  * ** complexitatea ​spațială ​**: $S = O(KMAX^3)$  
-    * explicatie ​ +    * explicație ​ 
-      * este nevoie ​sa stocam cateva matrici +      * este nevoie ​să stocăm câteva matrice 
-ObservatieIn ultimele calcule nu am sters contanta ​KMAX, intrucat ​apare la puterea a 3-a! $KMAX = 100$ implica ​$KMAX^3 = 10^6$, valoare care nu mai poate fi ignorata in practica ​($KMAX^3$ poate fi comparabil cu n).+**Observație!** În ultimele calcule nu am șters constanta ​KMAX, întrucât ​apare la puterea a 3-a! $KMAX = 100$ implică ​$KMAX^3 = 10^6$, valoare care nu mai poate fi ignorată în practică ​($KMAX^3$ poate fi comparabil cu n).
  
 === Gardurile lui Gigel (optimizare) === === Gardurile lui Gigel (optimizare) ===
-Dupa cum am vazut mai sus, in problema cu garduri ​data de Gigel solutia ​este o recurenta liniara:+După cum am văzut ​mai sus, în problema cu garduri ​dată de Gigel, soluția ​este o recurență liniară:
   *  $dp[1] = dp[2] = dp[3] = 1$; $d[4]=2$;   *  $dp[1] = dp[2] = dp[3] = 1$; $d[4]=2$;
   *  $dp[i] = dp[i - 1] + dp[i - 4]$, pentru $i > 4$   *  $dp[i] = dp[i - 1] + dp[i - 4]$, pentru $i > 4$
  
-== Exponentiere rapida :p ==+== Exponențiere rapidă ​==
   * $ k = 4 $   * $ k = 4 $
-  * $S_4 = (dp[1], dp[2], dp[3], dp[4]) = (1, 1, 1, 4)$+  * $S_4 = (dp[1], dp[2], dp[3], dp[4]) = (1, 1, 1, 2)$
   * $S_i = (dp[i-3], dp[i-2], dp[i-1], dp[i])$   * $S_i = (dp[i-3], dp[i-2], dp[i-1], dp[i])$
-  * Raspunsul ​se afla efectuand operatia ​$S_n = S_4 * C^{n - 4}$, unde C are urmatorul continut:  ​+  * Răspunsul ​se află efectuând operația ​$S_n = S_4 * C^{n - 4}$, unde C are următorul conținut:  ​
 \begin{gather} ​ \begin{gather} ​
    C = \begin{bmatrix}    C = \begin{bmatrix}
Line 534: Line 531:
 \end{gather} \end{gather}
  
-<spoiler Implementare ​in C++> +<spoiler Implementare ​în C++> 
-Mai jos se afla o implementare ​simplista in C++ care cuprinde toate etapele pe care trebuie ​sa le realizati in cod, dupa ce stiti cum arata recurenta ​sub forma matriceala.+Mai jos se află o implementare ​simplistă în C++ care cuprinde toate etapele pe care trebuie ​să le realizați în cod, după ce știți ​cum arată recurența ​sub forma matriceală.
    
 <code cpp> <code cpp>
Line 549: Line 546:
     for (int i = 0; i < KMAX; ++i) {     for (int i = 0; i < KMAX; ++i) {
         for (int j = 0; j < KMAX; ++j) {         for (int j = 0; j < KMAX; ++j) {
-            unsigned long long sum = 0; // presupun ​ca suma  ​incape ​pe 64 de biti+            unsigned long long sum = 0; // presupun ​că suma încape ​pe 64 de biți
  
             for (int k = 0; k < KMAX; ++k) {             for (int k = 0; k < KMAX; ++k) {
Line 576: Line 573:
         if  (p % 2 == 0) {         if  (p % 2 == 0) {
             multiply_matrix(C,​ C, C);     // C = C*C             multiply_matrix(C,​ C, C);     // C = C*C
-            p /= 2;                       // ​ramane ​de calculat C^(p/2)+            p /= 2;                       // ​rămâne ​de calculat C^(p/2)
         } else {         } else {
             // reduc la cazul anterior:             // reduc la cazul anterior:
             multiply_matrix(tmp,​ C, tmp); // tmp = tmp*C             multiply_matrix(tmp,​ C, tmp); // tmp = tmp*C
-            --p;                          // ramane ​de calculat C^(p-1)+            --p;                          // rămâne ​de calculat C^(p-1)
         }         }
     }     }
  
-    // avem o parte din rezultat ​in si o parte in tmp+    // avem o parte din rezultat ​în și o parte în tmp
     multiply_matrix(C,​ tmp, R);           // rezultat = tmp * C     multiply_matrix(C,​ tmp, R);           // rezultat = tmp * C
 } }
  
 int garduri_rapide(int n) { int garduri_rapide(int n) {
-    // cazurile de baza+    // cazurile de bază
     if (n <= 3) return 1;     if (n <= 3) return 1;
     if (n == 4) return 2;     if (n == 4) return 2;
Line 598: Line 595:
                           {0, 1, 0, 0},                           {0, 1, 0, 0},
                           {0, 0, 1, 1}};                           {0, 0, 1, 1}};
-   // vreau sa aplic formula S_n = S_4 * C^(n-4)+   // vreau să aplic formula S_n = S_4 * C^(n-4)
  
    // C = C^(n-4)    // C = C^(n-4)
    ​power_matrix(C,​ n - 4, C);    ​power_matrix(C,​ n - 4, C);
  
-   // sol = S_4 * C = dp[n] (se afla pe ultima ​pozitie ​din S_n, +   // sol = S_4 * C = dp[n] (se află pe ultima ​poziție ​din S_n, 
-   // deci voi folosi ultima ​coloana ​din C)+   // deci voi folosi ultima ​coloană ​din C)
    int sol = 1 * C[0][3] + 1 * C[1][3] + 1 * C[2][3] + 2 * C[3][3];    int sol = 1 * C[0][3] + 1 * C[1][3] + 1 * C[2][3] + 2 * C[3][3];
    ​return sol % MOD;    ​return sol % MOD;
Line 612: Line 609:
  
 <​note>​ <​note>​
-Remarcati ​faptul ​ca in functia ​de inmultire ​se foloseste ​o matrice ​temporara ​$tmp$. Motivul este ca vrem sa apelam functia ​$multiply(C,​ C, C)$, unde C joaca atat rol de intrare ​cat si de iesireDaca am pune rezultatele direct in C, atunci am strica inputul ​inainte sa obtinem ​rezultatul.+Remarcați ​faptul ​că în funcția ​de înmulțire ​se folosește ​o matrice ​temporară ​$tmp$. Motivul este că vrem să apelăm funcția ​$multiply(C,​ C, C)$, unde C joacă atât rol de intrare ​cât și de ieșireDacă am pune rezultatele direct in C, atunci am strica inputul ​înainte să obținem ​rezultatul.
  
-Putem spune ca acea functie ​este **matrix_multiply_safe**, ​in sensul ​ca pentru orice A,B,C care respecta ​dimensiunile impuse, ​functia ​va calcula corect produsul.+Putem spune că acea funcție ​este **matrix_multiply_safe**, ​în sensul ​că pentru orice A,B,C care respectă ​dimensiunile impuse, ​funcția ​va calcula corect produsul.
  
 </​note>​ </​note>​
 </​spoiler>​ </​spoiler>​
  
-<​spoiler ​Comparatie ​solutii (studiu de caz pentru ​curiosi)> +<​spoiler ​Comparație ​solutii (studiu de caz pentru ​curioși)> 
-In arhiva ** demo-lab04.zip ** gasiti ​sursa completa in care se realizeaza+Pe git găsiți ​sursă completă în care se realizează
-  * o verificare a faptului ​ca cele 2 implementari ​(** gardurile_lui_Gigel** ​si **garduri_rapide**) produc ​aceleasi ​rezultate +  * o verificare a faptului ​că cele 2 implementări ​(** gardurile_lui_Gigel** ​și **garduri_rapide**) produc ​aceleași ​rezultate 
-  * un benchmark ​in care cele 2 implementari ​sunt comparate +  * un benchmark ​în care cele 2 implementări ​sunt comparate 
-    * pe sistem uzual (laptop) s-au obtinut urmatoarele ​rezulate:+    * pe un sistem uzual (laptop) s-au obținut următoarele ​rezulate:
 <code bash> <code bash>
-test case: varianta ​simpla+test case: varianta ​simplă
 n = 100000000 sol = 119; time = 0.984545 s n = 100000000 sol = 119; time = 0.984545 s
-test case: varianta ​rapida+test case: varianta ​rapidă
 n = 100000000 sol = 119; time = 0.000021 s n = 100000000 sol = 119; time = 0.000021 s
  
  
-test case: varianta ​simpla+test case: varianta ​simplă
 n = 1000000000 sol = 812; time = 9.662377 s n = 1000000000 sol = 812; time = 9.662377 s
-test case: varianta ​rapida+test case: varianta ​rapidă
 n = 1000000000 sol = 812; time = 0.000022 s n = 1000000000 sol = 812; time = 0.000022 s
 </​code> ​ </​code> ​
-     * se observa ​clar diferenta intre cele 2 solutii ​(am confirmat ceea ce spunea ​si teoria: $O(n) $ vs $O(log(n))$); ​aceasta tehnica imbunatateste ​drastic o solutie gasita ​relativ usor.+     * se observă ​clar diferența între ​cele 2 soluții ​(am confirmat ceea ce spunea ​și teoria: $O(n) $ vs $O(log(n))$); ​această tehnică îmbunătățește ​drastic o soluție gasită ​relativ usor.
  
 </​spoiler>​ </​spoiler>​
    
-===== Exercitii ​=====+===== Exerciții ​=====
 <​note>​ <​note>​
-In acest laborator vom folosi scheletul ​de laborator ​din arhiva {{pa:new_pa:skel-lab04.zip}}.+Scheletul ​de laborator ​se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab04|pa-lab::skel/lab04]].
 </​note>​ </​note>​
- 
 === DP or math? === === DP or math? ===
-Fie un sir de **numere naturale strict pozitive**. ​Cate **subsiruri** (submultimi ​nevide) au suma numerelor **para**?+Fie un șir de **numere naturale strict pozitive**. ​Câte **subșiruri** (submulțimi ​nevide) au suma numerelor **pară**?
  
 <​note>​ <​note>​
-**subsir** (**subsequence** ​in engleza) pentru un vector ** v **inseamna ​un alt vector $u = [v[i_1], v[i_2],..., v[i_k]]]$ unde $i_1 < i_2 < ... < i_k$.+**subșir** (**subsequence** ​în engleză) pentru un vector **v** înseamnă ​un alt vector $u = [v[i_1], v[i_2],..., v[i_k]]]$ unde $i_1 < i_2 < ... < i_k$.
 </​note>​ </​note>​
  
  
 Task-uri: Task-uri:
-  * Se cere o **solutie ​folosind DP**. +  * Se cere o **soluție ​folosind DP**. 
-  * Inspectand recurenta gasita ​la punctul precedent, ​incercati sa inlocuiti ​cu o **formula matematica**. +  * Inspectând recurența gasită ​la punctul precedent, ​încercați să înlocuiți ​cu o **formulă matematică**. 
-  * Care este **complexitatea** pentru fiecare ​solutie ​(timp + spatiu)? Care este mai buna? De ce? :D+  * Care este **complexitatea** pentru fiecare ​soluție ​(timp + spațiu)? Care este mai bună? De ce? :D
  
-Deoarece rezultatul poate fi prea mare, se cere **restul ​impartirii** lui la $1000000007$ ($10^9 + 7$).+Deoarece rezultatul poate fi prea mare, se cere **restul ​împărțirii** lui la $1000000007$ ($10^9 + 7$).
  
-Pentru punctaj maxim pentru ​aceasta problema, este necesar ​sa rezolvati ​toate subpunctele (ex. nu folositi ​direct formula, ​gasiti ​mai intai recurenta ​DP). Trebuie ​sa implementati ​**cel putin** solutia ​cu DP.+Pentru punctaj maxim pentru ​această problemă, este necesar ​să rezolvați ​toate subpunctele (ex. nu folosiți ​direct formula, ​găsiți ​mai întâi recurența ​DP). Trebuie ​să implementați ​**cel puțin** soluția ​cu DP.
  
  
Line 668: Line 664:
 |v|2|6|4| |v|2|6|4|
  
-Raspuns: $7$+Răspuns: $7$
  
-Explicatie: Toate subsirule ​posibile sunt+Explicație: Toate subșirurile ​posibile sunt
   * $[2]$   * $[2]$
   * $[2, 6]$   * $[2, 6]$
Line 678: Line 674:
   * $[6, 4]$   * $[6, 4]$
   * $[4]$   * $[4]$
-Toate subsirurile ​de mai sus au suma para.+Toate subșirurile ​de mai sus au suma pară.
 </​spoiler>​ </​spoiler>​
  
Line 687: Line 683:
 |v|2|1|3| |v|2|1|3|
  
-Raspuns: $3$+Răspuns: $3$
  
-Explicatie: Toate subsirule ​posibile sunt+Explicație: Toate subșirurile ​posibile sunt
   * $[2]$   * $[2]$
   * $[2, 1]$   * $[2, 1]$
Line 698: Line 694:
   * $[3]$   * $[3]$
  
-Subsirurile ​cu suma para sunt: $[2]$, $[2, 1, 3]$, $[1, 3]$.+Subșirurile ​cu sumă pară sunt: $[2]$, $[2, 1, 3]$, $[1, 3]$.
  
 </​spoiler>​ </​spoiler>​
Line 708: Line 704:
 |v|3|2|1| |v|3|2|1|
  
-Raspuns: $3$+Răspuns: $3$
  
-Explicatie: Toate subsirule ​posibile sunt+Explicație: Toate subșirurile ​posibile sunt
   * $[3]$   * $[3]$
   * $[3, 2]$   * $[3, 2]$
Line 719: Line 715:
   * $[1]$   * $[1]$
  
-Subsirurile ​cu suma para sunt: $[3, 2, 1]$, $[3, 1]$, $[2]$.+Subșirurile ​cu sumă pară sunt: $[3, 2, 1]$, $[3, 1]$, $[2]$.
  
 </​spoiler>​ </​spoiler>​
  
 <​note>​ <​note>​
-Morala: ​exista ​probleme pentru care gasim solutie ​cu DP, dar pentru care poate exista ​si alte solutii ​mai bune (am ignorat citirea).+Morala: ​există ​probleme pentru care găsim ​soluție ​cu DP, dar pentru care pot exista ​și alte soluții ​mai bune (am ignorat citirea/afișarea).
  
-In problemele de numaratexista ​o **sansa** buna sa putem gasi (si) o formula matematicacare poate fi implementata intr-un mod mai eficient ​decat recurenta ​DP. +În problemele de număratexistă ​o **șansă** bună să putem si (și) o formulă matematicăce poate fi implementată într-un mod mai eficient ​decât ​recurență ​DP. 
 </​note>​ </​note>​
  
 <spoiler Hint> <spoiler Hint>
-Dar cate subsiruri ​au suma **impara**?+Câte subșiruri ​au suma **impară**?
 </​spoiler>​ </​spoiler>​
  
-<​spoiler ​Solutie+<​hidden>​ 
-Problema este preluata ​de [[https://​infoarena.ro/​problema/​azerah|aici]]. ​Solutia ​se gaseste ​[[https://​www.infoarena.ro/​onis-2015/​solutii-runda-1#​azerah|aici]].+<​spoiler ​Soluție
 +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>​ </​spoiler>​
 +</​hidden>​
  
-=== Expresie ​booleana ​=== +=== Expresie ​booleană ​=== 
-Se da o expresie ​booleana corecta ​cu n termeni. Fiecare din termeni poate fi unul din stringurile **true**, **false**, **and**, **or**, **xor**.+Se dă o expresie ​booleană corectă ​cu n termeni. Fiecare din termeni poate fi unul din stringurile **true**, **false**, **and**, **or**, **xor**.
  
-Numarati ​modurile ​in care se pot aseza paranteze astfel ​incat rezultatul ​sa fie **true**. Se respecta ​regulile de la logica ​(tabelele de adevar ​pentru ​operatiile ​**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**).
  
-Deoarece rezultatul poate fi prea mare, se cere **restul ​impartirii** lui la $1000000007$ ($10^9 + 7$).+Deoarece rezultatul poate fi prea mare, se cere **restul ​împărțirii** lui la $1000000007$ ($10^9 + 7$).
  
 <​note>​ <​note>​
-In schelet vom codifica cu valori de tip char cele 5 stringuri:+În schelet vom codifica cu valori de tip char cele 5 stringuri:
   * **false**: '​F'​   * **false**: '​F'​
   * **true**: '​T'​   * **true**: '​T'​
Line 752: Line 750:
   * **xor**: '​^'​   * **xor**: '​^'​
  
-Functia ​pe care va trebui ​sa implementati ​voi va folosi variabilele **n** (numarul ​de termeni) ​si ** expr** (vectorul cu termenii expresiei).+Funcția ​pe care va trebui ​să implementați ​voi va folosi variabilele **n** (numărul ​de termeni) ​și **expr** (vectorul cu termenii expresiei).
 </​note>​ </​note>​
  
  
 <spoiler Exemplu 1> <spoiler Exemplu 1>
-$n = 5$ si $expr = ['​T',​ '&',​ '​F',​ '​^',​ '​T'​]$ (expr = [** true and false xor true**])+$n = 5$ și $expr = ['​T',​ '&',​ '​F',​ '​^',​ '​T'​]$ (expr = [** true and false xor true**])
  
-Raspuns: $2$+Răspuns: $2$
  
-ExplicatieExista ​2 moduri corecte de a paranteza expresia astfel ​incat sa obtinem ​rezultatul **true** (1).+ExplicațieExistă ​2 moduri corecte de a paranteza expresia astfel ​încât să obținem ​rezultatul **true** (1).
   * $ T&(F^T) $   * $ T&(F^T) $
   * $ (T&F)^T $   * $ (T&F)^T $
Line 767: Line 765:
  
 <spoiler Hint> <spoiler Hint>
-    Complexitate temporală ​dorita ​este $O(n ^ 3)$.+    Complexitate temporală ​dorită ​este $O(n ^ 3)$.
     ​     ​
-    ​Optional, se pot defini ​functii ajutatoare ​precum **is_operand**,​ **is_operator**,​ **evaluate**.+    ​Opțional, se pot defini ​funcții ajutătoare ​precum **is_operand**,​ **is_operator**,​ **evaluate**.
 </​spoiler>​ </​spoiler>​
  
 <note tip> <note tip>
-Pentru rezolvarea celor doua probleme ​ganditi-va la ce scrie in sectiunea ​[[http://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​laborator-04?&#​sfaturireguli| Sfaturi / Reguli]]. Pentru fiecare dintre cele doua probleme facem o **partitionare dupa un anumit criteriu**.+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**.
  
  
-Pentru problema** DP or math?​** ​partitionam ​toate subsirurile dupa critieriul **paritatii ​sumei subsirului** (cate sunt pare/​impare).\\  +Pentru problema **DP or math?​** ​partiționăm ​toate subșirurile după critieriul **parității ​sumei subșirului** (câte sunt pare/​impare).\\  
-Pentru problema **expresie ​booleana** partitionam ​**toate ​parantezarile ​posibile ​dupa rezultatul lor** (cate dau true/​false).+Pentru problema **expresie ​booleană** partiționăm ​**toate ​parantezările ​posibile ​după rezultatul lor** (câte dau true/​false).
  
 </​note>​ </​note>​
 === Bonus === === Bonus ===
-Asistentul va alege una dintre problemele din sectiunea ​Extra.+Asistentul va alege una dintre problemele din secțiunea ​Extra.
  
 <spoiler Hint> <spoiler Hint>
-Recomandam sa ** NU** fie una din cele 3 probleme de la Test PA 2017. Recomandam sa le incercati dupa ce recapitulati acasa DP1 si DP2, pentru a verifica ​daca cunostintele ​acumulate sunt la nivelul ​asteptat.+Recomandăm să **NU** fie una dintre ​cele 3 probleme de la Test PA 2017. Recomandăm să le incercați după ce recapitulați acasă ​DP1 și DP2, pentru a verifica ​dacă cunoștințele ​acumulate sunt la nivelul ​așteptat.
 </​spoiler>​ </​spoiler>​
  
 === Extra === === Extra ===
-<​spoiler ​Extraterestrii+<​spoiler ​Extratereștrii
-Rezolvati ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v1-plumbus/​challenges/​test-1-extraterestrii +Rezolvați ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v1-plumbus/​challenges/​test-1-extraterestrii 
-extraterestrii]] de la Test PA 2017.+extratereștrii]] de la Test PA 2017.
 </​spoiler>​ </​spoiler>​
  
-<​spoiler ​Secvente+<​spoiler ​Secvențe
-Rezolvati ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v1-plumbus/​challenges/​test-1-secvente +Rezolvați ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v1-plumbus/​challenges/​test-1-secvente 
-Secvente]] de la Test PA 2017.+Secvențe]] de la Test PA 2017.
 </​spoiler>​ </​spoiler>​
  
Line 801: Line 799:
  
 <spoiler PA Country> <spoiler PA Country>
-Rezolvati ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v2-meeseeks/​challenges/​test-2-pa-country-medie+Rezolvați ​problema [[https://​www.hackerrank.com/​contests/​test-practic-pa-2017-v2-meeseeks/​challenges/​test-2-pa-country-medie
 | PA Country]] de la Test PA 2017. | PA Country]] de la Test PA 2017.
 </​spoiler>​ </​spoiler>​
Line 809: Line 807:
    
 <spoiler iepuri> <spoiler iepuri>
-Rezolvati ​pe infoarena problema [[http://​infoarena.ro/​problema/​iepuri| iepuri]].+Rezolvați ​pe infoarena problema [[http://​infoarena.ro/​problema/​iepuri| iepuri]].
  
-Hint: Exponentiere logaritmica ​pe matrice+Hint: Exponențiere logaritmică ​pe matrice
  
-Solutie:+Soluție:
   * $dp[0] = X; dp[1] = Y; dp[0] = Z; $   * $dp[0] = X; dp[1] = Y; dp[0] = Z; $
   * $dp[i] = (A * dp[i-1] + B * dp[i-2] + C * dp[i-3]) \ \% \ 666013$   * $dp[i] = (A * dp[i-1] + B * dp[i-2] + C * dp[i-3]) \ \% \ 666013$
  
-Pentru punctaj maxim, pentru fiecare test se foloseste ecuatia matriceala atasata.+Pentru punctaj maxim, pentru fiecare test se folosește ecuația matriceală atașată.
 Complexitate:​ $O(T * log(n))$. Complexitate:​ $O(T * log(n))$.
  
Line 824: Line 822:
  
 <spoiler Minimum Path Sum> <spoiler Minimum Path Sum>
-Rezolvati ​pe leetcode problema [[https://​leetcode.com/​problems/​minimum-path-sum/​description/#​| Minimum Path Sum]].+Rezolvați ​pe leetcode problema [[https://​leetcode.com/​problems/​minimum-path-sum/​description/#​| Minimum Path Sum]].
 </​spoiler>​ </​spoiler>​
  
  
-<​spoiler ​Lacusta+<​spoiler ​Lăcusta
-Rezolvati ​pe infoarena problema [[http://​infoarena.ro/​problema/​Lacusta| ​Lacusta]].+Rezolvați ​pe infoarena problema [[http://​infoarena.ro/​problema/​Lacusta| ​Lăcusta]].
 </​spoiler>​ </​spoiler>​
  
  
 <spoiler Suma4> <spoiler Suma4>
-Rezolvati ​pe infoarena problema [[http://​infoarena.ro/​problema/​Suma4|Suma4]].+Rezolvați ​pe infoarena problema [[http://​infoarena.ro/​problema/​Suma4|Suma4]].
 </​spoiler>​ </​spoiler>​
  
-<​spoiler ​Subsir+<​spoiler ​Subșir
-Rezolvati ​pe infoarena problema [[https://​www.infoarena.ro/​problema/​subsir|subsir]].+Rezolvați ​pe infoarena problema [[https://​www.infoarena.ro/​problema/​subsir|subșir]].
 </​spoiler>​ </​spoiler>​
  
-<​spoiler ​2sah+<​spoiler ​2șah
-Rezolvati ​pe infoarena problema [[https://​infoarena.ro/​problema/​2sah | 2sah]].+Rezolvați ​pe infoarena problema [[https://​infoarena.ro/​problema/​2sah | 2șah]].
  
-Hint:   Exponentiere logaritmica ​pe matrice+Hint: Exponențiere logaritmică ​pe matrice
  
-descrie detaliata ​se afla in [[http://​olimpiada.info/​oji2015/​index.php?​cid=arhiva | arhiva OJI 2015]].+descriere detaliată ​se află în [[http://​olimpiada.info/​oji2015/​index.php?​cid=arhiva | arhiva OJI 2015]].
 </​spoiler>​ </​spoiler>​
-===== Referințe ===== 
  
-[0] Capitolul **Dynamic Programming** din **Introductions to Algorithms** ​de către T. H. CormenC. E. Leiserson, R. L. Rivest, CStein+<spoiler DP problems>​ 
 +Articolul de pe [[https://​leetcode.com/​discuss/​general-discussion/​458695/​Dynamic-Programming-Patterns| leetcode]] conține o listă cu diverse tipuri de probleme ​de programare dinamică, din toate categoriile discutate la PA. 
 +</​spoiler>​
  
 +===== Referințe =====
 +
 +[0] Chapter **Dynamic Programming**,​ “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein
 + 
 [1] [[http://​infoarena.ro/​problema/​podm]] [1] [[http://​infoarena.ro/​problema/​podm]]
  
pa/laboratoare/laborator-04.txt · Last modified: 2023/03/31 11:47 by radu.iacob
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0