Differences

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

Link to this comparison view

pa:laboratoare:laborator-03 [2019/10/18 08:14]
darius.neatu [SCMAX]
pa:laboratoare:laborator-03 [2023/03/15 16:54] (current)
radu.nichita
Line 1: Line 1:
-====== Laborator 3: Programare Dinamică ====== +====== Laborator 03: Programare Dinamică ​(1/2) ======
-Responsabili:​ +
-  * [[neatudarius@gmail.com|Darius Neațu]] +
-  * [[visanr95@gmail.com|Radu Vișan]] +
-  * [[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 programare dinamică 
-  * Insușirea abilităților de implementare a algoritmilor bazați programare dinamică.+  * însușirea abilităților de implementare a algoritmilor bazați programare dinamică.
  
-===== Precizari initiale ​=====+===== Precizări inițiale ​=====
 <​note>​ <​note>​
-Toate exemplele de cod se gasesc in {{pa:new_pa:demo-lab03.zip}}.+Toate exemplele de cod se găsesc pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​demo/​lab03|pa-lab::demo/lab03]].
  
-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 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. 
-  * Va rugam sa incercati si codul din arhiva ​** demo-lab03.zip**, inainte ​de a raporta ca ceva nu merge+  * Vă rugam 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ă ​in general pentru rezolvarea **problemelor de optimizare**.  +Similar cu greedy, tehnica de programare ​dinamică ​este folosită ​în general 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** (vom exemplifica ​in lab04).+De asemenea, DP se poate folosi ​și pentru probleme ​în care nu căutăm ​un optim, cum ar fi **problemele de numărare** (vom exemplifica ​în lab04).
 ===== Aplicatii DP ===== ===== Aplicatii DP =====
-Programarea dinamică are un **câmp larg de aplicare**, ​insa la PA ne vom rezuma la cateva aplicatii ​care vor fi mentionate ​pe parcursul laboratoarelor 3 si 4. De asemenea, ​aceasta tehnica ​va fi folosita si in laboratoarele de grafuri (ex. algoritmul Floyd-Warshall - pe care il veti implementa ​si la PA; algoritmi pe arbori etc).+Programarea dinamică are un **câmp larg de aplicare**, ​însă ​la PA ne vom rezuma la câteva aplicații ​care vor fi menționate ​pe parcursul laboratoarelor 3 și 4. De asemenea, ​această tehnică ​va fi folosită și în laboratoarele de grafuri (ex. algoritmul Floyd-Warshall - pe care îl veți implementa ​și la PA; algoritmi pe arbori etc).
  
-Programare ​dinamică presupune rezolvarea unei probleme prin **descompunerea ei în subprobleme** şi rezolvarea acestora. Spre deosebire de divide et impera, subproblemele nu sunt disjuncte, ci **se suprapun**.+Programarea ​dinamică presupune rezolvarea unei probleme prin **descompunerea ei în subprobleme** şi rezolvarea acestora. Spre deosebire de divide et impera, subproblemele nu sunt disjuncte, ci **se suprapun**.
  
 <spoiler Memoizare and more DP> <spoiler Memoizare and more DP>
 Pentru a evita recalcularea porțiunilor care se suprapun, rezolvarea se face pornind de la cele mai mici subprobleme şi folosindu-ne de rezultatul acestora calculăm subproblema imediat mai mare. Cele mai mici subprobleme sunt numite subprobleme unitare, acestea putând fi rezolvate într-o complexitate constantă, ex: cea mai mare subsecvență dintr-o mulțime de un singur element. Pentru a evita recalcularea porțiunilor care se suprapun, rezolvarea se face pornind de la cele mai mici subprobleme şi folosindu-ne de rezultatul acestora calculăm subproblema imediat mai mare. Cele mai mici subprobleme sunt numite subprobleme unitare, acestea putând fi rezolvate într-o complexitate constantă, ex: cea mai mare subsecvență dintr-o mulțime de un singur element.
  
-Pentru a nu recalcula soluțiile subproblemelor ce ar trebui rezolvate de mai multe ori, pe ramuri diferite, se reține soluția subproblemelor folosind o tabelă (matrice uni, bi sau multi-dimensională în funcție de problemă) cu rezultatul fiecărei subprobleme. ​Aceasta tehnica ​se numește **memoizare**.+Pentru a nu recalcula soluțiile subproblemelor ce ar trebui rezolvate de mai multe ori, pe ramuri diferite, se reține soluția subproblemelor folosind o tabelă (matrice uni, bi sau multi-dimensională în funcție de problemă) cu rezultatul fiecărei subprobleme. ​Această tehnică ​se numește **memoizare**.
  
-Aceasta ​tehnică ​determina ​”valoarea” soluției pentru fiecare din subprobleme. Mergând de la subprobleme mici la subprobleme din ce în ce mai mari ajungem la soluția optimă, la nivelul întregii probleme. ”valoarea” is schimba ​înțelesul logic de la o problema ​la alta. În probleme ​de minimizarea costului, ”valoarea” este reprezentata ​de costul minim. ​In probleme care presupun identificarea unei componente maxime, ”valoarea” este caracterizată de dimensiunea componentei.+Această ​tehnică ​determină ​”valoarea” soluției pentru fiecare din subprobleme. Mergând de la subprobleme mici la subprobleme din ce în ce mai mari ajungem la soluția optimă, la nivelul întregii probleme. ”Valoarea” își schimbă ​înțelesul logic de la o problemă ​la alta. În problemele ​de minimizarea costului, ”valoarea” este reprezentată ​de costul minim. ​În probleme care presupun identificarea unei componente maxime, ”valoarea” este caracterizată de dimensiunea componentei.
  
 După calcularea valorii pentru toate subproblemele se poate determina efectiv mulțimea de elemente care compun soluția. „Reconstrucția” soluţiei se face mergând din subproblemă în subproblemă,​ începând de la problema cu valoarea optimă și ajungând în subprobleme unitare. Metoda și recurența variază de la problemă la problemă, dar în urma unor exerciții practice va deveni din ce în ce mai facil să le identificați. După calcularea valorii pentru toate subproblemele se poate determina efectiv mulțimea de elemente care compun soluția. „Reconstrucția” soluţiei se face mergând din subproblemă în subproblemă,​ începând de la problema cu valoarea optimă și ajungând în subprobleme unitare. Metoda și recurența variază de la problemă la problemă, dar în urma unor exerciții practice va deveni din ce în ce mai facil să le identificați.
Line 42: Line 38:
 </​spoiler>​ </​spoiler>​
  
-Cei curiosi ​pot citi [[https://​en.wikipedia.org/​wiki/​Dynamic_programming#​History|aici]] ​adevarul ​despre numele acestei tehnici. 8-)+Cei curioși ​pot citi [[https://​en.wikipedia.org/​wiki/​Dynamic_programming#​History|aici]] ​adevărul ​despre numele acestei tehnici. 8-)
  
 ===== Ce determina DP? ===== ===== Ce determina DP? =====
-Aplicând ​aceasta ​tehnică determinăm **una din soluțiile optime**, problema putând avea mai multe soluții optime. În cazul în care se dorește determinarea tuturor soluțiilor optime, algoritmul trebuie combinat cu unul de backtracking în vederea construcției soluțiilor.+Aplicând ​această ​tehnică determinăm **una din soluțiile optime**, problema putând avea mai multe soluții optime. În cazul în care se dorește determinarea tuturor soluțiilor optime, algoritmul trebuie combinat cu unul de backtracking în vederea construcției soluțiilor.
  
-In cazul problemelor de numarareaceasta tehnica ​ne va furniza **numarul cautat**.+În cazul problemelor de numărareaceastă tehnică ​ne va furniza **numărul căutat**.
 ===== Tipar general DP ===== ===== Tipar general DP =====
 Aplicarea acestei tehnici de programare poate fi descompusă în următoarea secvență de pași: Aplicarea acestei tehnici de programare poate fi descompusă în următoarea secvență de pași:
Line 57: Line 53:
 ===== 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ă ​din programare. 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ța ​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 Matrici) 
-  *  ​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 1: SSM ===== ===== Categoria 1: SSM =====
-Aceste ​recurente ​au o oarecare ​asemanare ​cu problema SSM (enunt solutie).+Aceste ​recurențe ​au o oarecare ​asemănare ​cu problema SSM (enunț ​soluție). 
 + 
 + 
 + 
 +<​note>​ 
 +ATENȚIE! Rețineți diferența între următoarele 2 noțiuni! 
 + 
 + 
 +* **subsecvență** ([[https://​en.wikipedia.org/​wiki/​Substring | substring]] în engleză) pentru un vector ** v ** înseamnă un alt vector $u = [v[i], v[i+1],..., v[j]]$ unde $i <= j$. 
 + 
 +* **subșir** ([[https://​en.wikipedia.org/​wiki/​Subsequence | 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> ​
  
 ==== SSM ==== ==== SSM ====
-=== Enunt === +=== Enunț ​=== 
-Fie un vector $ v $ cu $ n $ elemente ​intregi. O subsecventa ​de numere din sir este de forma: $s_is_{i+1}, ... , s_j$ ($i <= j$), avand suma asociata ​$s_{ij} = s_i +  ​s_{i+1} + ... + s_j$. O subsecventa ​** nu ** poate fi vida.+Fie un vector $ v $ cu $ n $ elemente ​întregi. O subsecvență ​de numere din șir este de forma: $v_iv_{i+1}, ... , v_j$ ($i <= j$), având ​suma asociată ​$s_{ij} = v_i +  ​v_{i+1} + ... + v_j$. O subsecvență ​** nu ** poate fi vidă. 
 + 
  
-=== Cerinta ​=== +=== Cerință ​=== 
-Sa se determine ​subsecventa ​de suma maxima ​(notata ​**SSM**).+Să se determine ​subsecvența ​de sumă maximă ​(notată ​**SSM**).
  
 === Exemple === === Exemple ===
Line 87: Line 98:
 |v[i]| -10 | 2 | 3 | -1| 2 | -3 | |v[i]| -10 | 2 | 3 | -1| 2 | -3 |
  
-Raspuns: SSM este intre si 5 (pozitii). Are suma +6. ($SSM = 2, 3, -1, 2$)+Răspuns: SSM este între ​și 5 (poziții). Are suma +6. ($SSM = 2, 3, -1, 2$)
  
-Explicatie: avem numere pozitive, deci exista ​solutie simpla in care putem sa alegem doar un numar pozitiv/mai multe numere pozitive de pe pozitii alaturate ​(adica incercam sa evitam ​numere negative). Cele mai lungi subsecvente ​cu numere pozitive sunt 2,3 si 2. Observam ca daca extindem $2, 3$ la $2, 3, -1, 2$, desi am inclus un numar negativ, suma secventei creste+Explicație: avem numere pozitive, deci există ​soluție simplă în care putem să alegem doar un număr ​pozitiv/mai multe numere pozitive de pe poziții alăturate ​(adică încercăm să evităm ​numere negative). Cele mai lungi subsecvențe ​cu numere pozitive sunt 2,3 și 2. Observăm că dacă extindem $2, 3$ la $2, 3, -1, 2$, deși am inclus un număr ​negativ, suma secvenței crește
 </​spoiler>​ </​spoiler>​
  
Line 98: Line 109:
 |v[i]| 10 | 20 | 30 | 40| |v[i]| 10 | 20 | 30 | 40|
  
-Raspuns: SSM este intre si 4 (pozitii). Are suma 100. ($SSM = 10, 20, 30, 40$)+Răspuns: SSM este între ​și 4 (poziții). Are suma 100. ($SSM = 10, 20, 30, 40$)
  
-Explicatie: deoarece toate numerele sunt **pozitive**,​ SSM cuprinde toate numerele.+Explicație: deoarece toate numerele sunt **pozitive**,​ SSM cuprinde toate numerele.
 </​spoiler>​ </​spoiler>​
  
Line 109: Line 120:
 |v[i]| -10 | -20 | -30 | -40| |v[i]| -10 | -20 | -30 | -40|
  
-Raspuns: SSM este intre 1 si 1 (pozitii). Are suma -10. ($SSM = -10$)+Răspuns: SSM este între ​1 si 1 (poziții). Are suma -10. ($SSM = -10$)
  
-Explicatie: deoarece toate numerele sunt **negative**,​ SSM cuprinde doar cel mai mare numar.+Explicație: deoarece toate numerele sunt **negative**,​ SSM cuprinde doar cel mai mare număr.
 </​spoiler>​ </​spoiler>​
  
 === Rezolvare === === Rezolvare ===
 == Tipar == == Tipar ==
-Tiparul acestei probleme ne sugereaza ca solutie ​este obtinuta ​incremental, ​in sensul ​ca **putem** privi problema astfel: ​gasim cea mai buna solutie ​folosind **primele $i-1$** elemente din sir, apoi incercam sa o **extindem** folosind elementul **i** (adica ne extindem la dreapta ~CU~ $v[i]$).+Tiparul acestei probleme ne sugerează că soluție ​este obținută ​incremental, ​în sensul ​că **putem** privi problema astfel: ​găsim ​cea mai bună soluție ​folosind **primele $i-1$** elemente din șir, apoi încercăm să o **extindem** folosind elementul **i** (adică ​ne extindem la dreapta ~CU~ $v[i]$).
  
-== Numire ​recurenta ​== +== Numire ​recurență ​== 
-Intrucat ​la fiecare pas trebuie sa retinem ​** cea mai buna solutie** folosind un **prefix** din vectorul v, solutia ​va fi salvata intr-un tablou auxiliar definit astfel:+Întrucât ​la fiecare pas trebuie sa reținem ​** cea mai bună soluție** folosind un **prefix** din vectorul v, soluția ​va fi salvată într-un tablou auxiliar definit astfel:
  
-$ dp[i] $ = suma subsecventei ​de suma maxima ​(**suma SSM**) folosind ** doar primele ​ i ** elemente din vectorul v si care se termina ​pe pozitia ​i+$ dp[i] $ = suma subsecvenței ​de sumă maximă ​(**suma SSM**) folosind ** doar primele ​ i ** elemente din vectorul v și care **se termină ​pe poziția ​i**
  
-== Mentiuni ​== +== Mențiuni ​== 
-  * Pentru a mentine ​conventie, toate tablourile de acest tip din laborator vor fi notate cu ** dp ** (dynamic programming). +  * Pentru a menține ​convenție, toate tablourile de acest tip din laborator vor fi notate cu ** dp ** (dynamic programming). 
-  * Ca sa rezolvam ​problema ​data, trebuie ​sa rezolvam ​multime ​de subprobleme +  * Ca să rezolvăm ​problema ​dată, trebuie ​să rezolvăm ​mulțime ​de subprobleme 
-    * $dp[i]$ ​reprezinta ​**solutia** pentru problema $v[1], ..., v[i]$ si care se termina ​cu $v[i]$ +    * $dp[i]$ ​reprezintă ​**soluția** pentru problema $v[1], ..., v[i]$ și care se termină ​cu $v[i]$ 
-  * Solutia ​pentru problema ​initiala ​este maximul din vectorul $dp[i]$.+  * Soluția ​pentru problema ​inițială ​este maximul din vectorul $dp[i]$ ​- a.k.a. **max(dp[i])**.
  
-== Gasire recurenta ​== +== Găsire recurență ​== 
-Intrucat ​dorim ca aceasta problema sa fie rezolvabila ​printr-un algoritm/bucata ​de cod, trebuie ​sa descriem o metoda concreta ​prin care vom calcula $dp[i]$.+Întrucât ​dorim ca această problemă să fie rezolvabilă ​printr-un algoritm/bucată ​de cod, trebuie ​să descriem o metodă concretă ​prin care vom calcula $dp[i]$.
  
-  * **Cazul de baza** +  * **Cazul de bază** 
-    * In general ​in probleme putem avea mai multe cazuri de baza, care in principiu se leaga de valori extreme are dimensiunilor subproblemelor. +    * În general ​în probleme putem avea mai multe cazuri de bază, care în principiu se leagă ​de valori extreme are dimensiunilor subproblemelor. 
-    * In cazul SSM, avem un singur caz de bazacand  ​avem un singur element ​in prefix: $dp[1] = v[1] $. +    * În cazul SSM, avem un singur caz de bazăcând avem un singur element ​în prefix: $dp[1] = v[1] $. 
-    * Explicatiedaca avem un singur element, atunci acesta ​formeaza ​singura ​subsecventa posibila, deci $ SSM = v[1] $+    * Explicațiedacă avem un singur element, atunci acesta ​formează ​singura ​subsecvență posibilă, deci $ SSM = v[1] $
   ​   ​
   * ** Cazul general **   * ** Cazul general **
-     * presupune inductiv ​ca avem rezolvate toate subproblemele mai mici +     * presupune inductiv ​că avem rezolvate toate subproblemele mai mici 
-     ​* ​in cazul SSM, presupunem ​ca avem calculat $ dp[i-1] $ si dorim sa calculam ​$ dp[i] $ (cunoastem ​cea mai buna solutie ​folosind primele i-1 elememente ​si vedem daca elementul de pe pozitia ​i o poate imbunatati+     ​* ​în cazul SSM, presupunem ​că avem calculat $ dp[i-1] $ și dorim sa calculăm ​$ dp[i] $ (cunoaștem ​cea mai bună soluție ​folosind primele i-1 elememente ​și vedem dacă elementul de pe poziția ​i o poate îmbunătăți
-     * la fiecare pas avem de ales daca $v[i]$ extinde cea mai buna solutie ​care se termina ​pe $v[i-1]$ sau se incepe ​noua secventa ​cu $v[i]$ +     * la fiecare pas avem de ales dacă $v[i]$ extinde cea mai bună soluție ​care se termină ​pe $v[i-1]$ sau se începe ​nouă secvență ​cu $v[i]$ 
-     * decidem ​in functie ​de $ dp[i - 1]$ si $v[i] $ +     * decidem ​în funcție ​de $ dp[i - 1]$ și $v[i] $ 
-      * ** daca ** $ dp[i - 1] >= 0 $ (cea mai buna solutie ​care se termina ​pe i - 1 are cost nenegativ) +      * ** dacă ** $ dp[i - 1] >= 0 $ (cea mai bună soluție ​care se termină ​pe i - 1 are cost nenegativ) 
-        * extindem ​secventa ​care se termina ​cu v[i-1] folosind elementul v[i]: $dp[i] = dp[i-1] + v[i]$ +        * extindem ​secvență ​care se termină ​cu v[i-1] folosind elementul v[i]: $dp[i] = dp[i-1] + v[i]$ 
-        * Explicatie: $dp[i-1] + v[i] >= v[i]$ (inca are rost sa extind) +        * Explicație: $dp[i-1] + v[i] >= v[i]$ (încă ​are rost să extind) 
-      * ** daca ** $ dp[i - 1] < 0 $ (cea mai buna solutie ​care se termina ​pe i - 1 are cost negativ) +      * ** dacă ** $ dp[i - 1] < 0 $ (cea mai bună soluție ​care se termină ​pe i - 1 are cost negativ) 
-        * vom incepe ​noua secventa ​cu $v[i]$, ​adica $dp[i] = v[i]$ +        * vom începe ​nouă secvență ​cu $v[i]$, ​adică ​$dp[i] = v[i]$ 
-        * Explicatie: $v[i] > dp[i-1] + v[i]$, deci prin extindere nu obtin solutie maxima!+        * Explicație: $v[i] > dp[i-1] + v[i]$, deci prin extindere nu obțin soluție maximă!
  
-== Implementare ​recurenta ​== +== Implementare ​recurență ​== 
-In majoritatea problemelor de DP, gasirea recurentei ocupa cea mai mare parte a timpului de rezolvare (lucru ​adevarat si in cazul problemelor de la PA). De aceea, faptul ​ca ati reusit sa scrieti ​pe foaie lucruri foarte complicate poate fi un indiciu ca ati pornit pe o cale gresita.+În majoritatea problemelor de DP, găsirea recurenței ocupă ​cea mai mare parte a timpului de rezolvare (lucru ​adevărat și în cazul problemelor de la PA). De aceea, faptul ​că ați reușit să scrieți ​pe foaie lucruri foarte complicate poate fi un indiciu ca ați pornit pe o cale greșită.
  
 <spoiler Exemplu implementare>​ <spoiler Exemplu implementare>​
  
-Mai jos se afla un exemplu simplu de implementare a recurentei gasite in C++.+Problema se poate testa pe infoarena: [[https://​www.infoarena.ro/​problema/​ssm | Subsecvență de sumă maximă]]. 
 + 
 +Mai jos se află un exemplu simplu de implementare a recurenței găsite în C++.
  
 <code cpp> <code cpp>
  
-// gaseste ​SSM pentru vectorul v cu n elemente +// găsește ​SSM pentru vectorul v cu n elemente 
-// pentru a mentine conventia ​din explicatii:+// pentru a menține convenția ​din explicații:
 //      - elementele sunt indexate de la 0, dar le folosesc doar pe cele care incep de la 1 //      - elementele sunt indexate de la 0, dar le folosesc doar pe cele care incep de la 1
 //                                          => v[1], ..., v[n] //                                          => v[1], ..., v[n]
 int SSM(int n, vector<​int>​ &v) { int SSM(int n, vector<​int>​ &v) {
- vector<​int>​ dp(n + 1);    // vector cu n + 1 elemente (indexarea ​incepe ​de la 0)+ vector<​int>​ dp(n + 1);    // vector cu n + 1 elemente (indexarea ​începe ​de la 0)
                                   // am nevoie de dp[1], ..., dp[n]                                   // am nevoie de dp[1], ..., dp[n]
  
- // caz de baza+ // caz de bază
  dp[1] = v[1];  dp[1] = v[1];
  
Line 175: Line 188:
  dp[i] = dp[i - 1] + v[i];  dp[i] = dp[i - 1] + v[i];
  } else {  } else {
- // incep noua secventa+ // încep ​nouă secvență
  dp[i] = v[i];  dp[i] = v[i];
  }  }
  }  }
  
- // solutia ​e maximul din vectorul dp+ // soluția ​e maximul din vectorul dp
  int sol = dp[1];  int sol = dp[1];
  for (int i = 2; i <= n; ++i) {  for (int i = 2; i <= n; ++i) {
Line 188: Line 201:
  }  }
  
-        return sol; // aceasta este suma asociata ​cu SSM+        return sol; // aceasta este suma asociată ​cu SSM
 } }
  
 </​code>​ </​code>​
  
-Daca dorim sa afisam si indicii ​intre care apare SSM, putem sa stocam si pozitia ​de start pentru fiecare ​solutie intermediaraGasiti aceasta solutie in ** demo-lab03.zip**+Dacă dorim să afișăm și indicii ​între ​care apare SSM, putem să stocăm și poziția ​de start pentru fiecare ​soluție intermediară. . 
-Hint: definiti ​** start[i] ** = pozitia ​pe care a inceput subsecventa ​care da solutia ​cu cost dp[i].+Hint: definiți ​** start[i] ** = poziția ​pe care a început subsecvența ​care dă soluția ​cu cost dp[i].
  
 </​spoiler>​ </​spoiler>​
  
-=== Mentiuni ​=== +=== Mențiuni ​=== 
-Intrucat aceasta solutie ​presupune calculul iterativ (coloana ​cu coloana) a matricei dp, complexitatea este liniara. De asemenea, se mai parcurge o data dp pentru a gasi maximul.  +Întrucât această soluție ​presupune calculul iterativ (coloană ​cu coloană) a matricei dp, complexitatea este liniară. De asemenea, se mai parcurge o dată dp pentru a găsi maximul.  
-  * **complexitate ​temporala ​**: $T = O(n)$ +  * **complexitate ​temporală ​**: $T = O(n)$ 
-  * **complexitate ​spatiala ​** : $S = O(n)$ +  * **complexitate ​spațială ​** : $S = O(n)$ 
-         * desigur ​ca pentru problema SSM, nu era nevoie sa retinem, tablourile dp/​start ​in memorie. +         * desigur ​că pentru problema SSM, nu era nevoie sa reținem, tablourile dp/​start ​în memorie. 
-         * puteam sa construim element cu element ​si maximul din dp in aceleasi ​timp (intrucat ​ne trebuie ultima valoare la fiecare pas si maximul global). +         * puteam sa construim element cu element ​și maximul din dp în aceleași ​timp (întrucât ​ne trebuie ultima valoare la fiecare pas și maximul global). 
-         ​* ​in acest caz complexitatea ​spatiala ​devine $S = O(1)$ +         ​* ​în acest caz complexitatea ​spațială ​devine $S = O(1)$  
 + 
 +** Pentru a ilustra toți pașii posibili într-o astfel de problemă, totul a fost prezentat cât mai simplu (NU în toate problemele putem facem simplificări de tipul "NU am nevoie să stochez tabloul dp"​).**
  
-** Pentru a ilustra toti pasii posibili intr-o astfel de problema, totul a fost prezentat cat mai simplu (NU in toate problemele putem facem simplificari de tipul "NU am nevoie sa stochez tabloul dp"​).** 
            
 ==== SCMAX ==== ==== SCMAX ====
-=== Enunt === +=== Enunț ​=== 
-Fie un vector $ v $ cu $ n $ elemente ​intregi. Un subsir ​de numere din sir este de forma: $s_{i_1}, ​s_{i_2}, ... , {s_{i_k}}$. Un subsir ​** nu ** poate fi vid ($k >= 1$).+Fie un vector $ v $ cu $ n $ elemente ​întregi. Un subșir ​de numere din șir este de forma: $v_{i_1}, ​v_{i_2}, ... , v_{i_k}$. Un subșir ​** nu ** poate fi vid ($k >= 1$).
  
-=== Cerinta ​=== +=== Cerința ​=== 
-Sa se determine ​subsirul crescator ​maximal (notat **SCMAX**) - un subsir ​ordonat strict ​crescator si are lungime ​maxima ​(daca sunt mai multe solutiisa se gaseasca ​una oarecare).+Să se determine ​subșirul crescător ​maximal (notat **SCMAX**) - un subșir ​ordonat strict ​crescător și are lungime ​maximă ​(dacă sunt mai multe soluțiisă se gasească ​una oarecare).
  
 === Exemple === === Exemple ===
Line 222: Line 236:
 |v[i]| 100 | 12 | 13 | -1| 15 | -30 | |v[i]| 100 | 12 | 13 | -1| 15 | -30 |
  
-Raspuns: $SCMAX = 12, 13, 15$ ($SCMAX = v[2], v[3], v[5])$.+Răspuns: $SCMAX = 12, 13, 15$ ($SCMAX = v[2], v[3], v[5])$.
  
-Explicatie:  +Explicație:  
-Toate subsirurile ​ordonate strict ​crescator ​sunt:+Toate subșirurile ​ordonate strict ​crescător ​sunt:
   * $100$   * $100$
   * $12$   * $12$
Line 237: Line 251:
   * $15$   * $15$
   * $-30$   * $-30$
-Cel mentionat ​este singurul de lungime 3.+Cel menționat ​este singurul de lungime 3.
 </​spoiler>​ </​spoiler>​
  
Line 246: Line 260:
 |v[i]| 100 | 12 | 13 | -1| 15 | 14 | |v[i]| 100 | 12 | 13 | -1| 15 | 14 |
  
-Raspuns:+Răspuns:
   * $SCMAX = 12, 13, 15$ ($SCMAX = v[2], v[3], v[5])$.   * $SCMAX = 12, 13, 15$ ($SCMAX = v[2], v[3], v[5])$.
   * $SCMAX = 12, 13, 14$ ($SCMAX = v[2], v[3], v[6])$.   * $SCMAX = 12, 13, 14$ ($SCMAX = v[2], v[3], v[6])$.
  
-Explicatie:  +Explicație:  
-Toate subsirurile ​ordonate strict ​crescator ​sunt:+Toate subșirurile ​ordonate strict ​crescător ​sunt:
   * $100$   * $100$
   * $12$   * $12$
Line 265: Line 279:
   * $15$   * $15$
   * $14$   * $14$
-Cele 2 solutii ​indicate au ambele lungime ​maxima.+Cele 2 soluții ​indicate au ambele lungime ​maximă.
  
 </​spoiler>​ </​spoiler>​
Line 271: Line 285:
 === Rezolvare === === Rezolvare ===
 == Tipar== == Tipar==
-Verificam daca se aplica ​tiparul de la SSM: **gasim cea mai buna solutie ​folosind primele $i-1$ elemente din sir, apoi incercam sa o extindem folosind elementul i (adica ne extindem la dreapta ~CU~ $v[i]$)**.+Verificăm dacă se aplică ​tiparul de la SSM: **găsim ​cea mai buna soluție ​folosind primele $i-1$ elemente din șir, apoi încercăm să o extindem folosind elementul i (adică ​ne extindem la dreapta ~CU~ $v[i]$)**.
  
-    * Daca avem cea mai buna solutie ​pentru intervalul $1, 2, .., i-1$ si care se termina ​cu $v[i-1]$, atunci ​incercam sa extindem ​solutia ​cu $v[i]$ (putem ​daca $v[i-1] < v[i]$) +    * Dacă avem cea mai bună soluție ​pentru intervalul $1, 2, .., i-1$ și care se termină ​cu $v[i-1]$, atunci ​încercăm să extindem ​soluția ​cu $v[i]$ (putem ​dacă $v[i-1] < v[i]$) 
-    * Altfel.. Unde am putea sa il punem pe $v[i]$? +    * Altfel.. Unde am putea să îl punem pe $v[i]$? 
-      * Pai am putea sa incercam sa il punem la finalul ​solutiei ​care se termina ​pe $v[i-2]$, $v[i-3]$, ... sau $v[1]$+      * Păi am putea sa încercăm să îl punem la finalul ​soluției ​care se termină ​pe $v[i-2]$, $v[i-3]$, ... sau $v[1]$
  
-== Numire ​recurenta ​== +== Numire ​recurență ​== 
-$ dp[i] $ = lungimea celui mai lung subsir(**lungime SCMAX**) folosind (doar o parte) din primele i elemente din vectorul v si care se termina ​pe pozitia ​i+$ dp[i] $ = lungimea celui mai lung subșir(**lungime SCMAX**) folosind (doar o parte) din primele i elemente din vectorul v și care se termină ​pe poziția ​i
  
-== Mentiuni ​== +== Mențiuni ​== 
-  * Ca sa rezolvam ​problema ​data, trebuie ​sa rezolvam ​multime ​de subprobleme +  * Ca să rezolvăm ​problema ​dată, trebuie ​să rezolvăm ​mulțime ​de subprobleme 
-    * $dp[i]$ ​reprezinta ​**solutia** pentru problema $v[1], ..., v[i]$ si care se termina ​cu $v[i]$ +    * $dp[i]$ ​reprezintă ​**soluția** pentru problema $v[1], ..., v[i]$ și care se termină ​cu $v[i]$ 
-  * Solutia ​pentru problema ​initiala ​este maximul din vectorul $dp[i]$.+  * Soluția ​pentru problema ​inițială ​este maximul din vectorul $dp[i]$.
  
-== Gasire recurenta ​== +== Găsire recurență ​== 
-  * **Cazul de baza** +  * **Cazul de bază** 
-    * Si in problema SCMAX, cazul pentru $i = 1$ este caz de baza.  +    * Și în problema SCMAX, cazul pentru $i = 1$ este caz de bază.  
-          * daca avem un singur element, atunci avem o singura subsecventa ​de lungime 1, ea este solutia+          * dacă avem un singur element, atunci avem o singură subsecvență ​de lungime 1, ea este soluția
           * $dp[1] = 1$  ​           * $dp[1] = 1$  ​
   ​   ​
   * ** Cazul general **   * ** Cazul general **
-      * presupune inductiv ​ca avem rezolvate toate subproblemele mai mici +      * presupune inductiv ​că avem rezolvate toate subproblemele mai mici 
-      * in cazul SCMAX, presupunem ​ca avem calculate $ dp[1], dp[2], ..., dp[i-1] $ si dorim sa calculam ​$ dp[i] $ (cunoastem ​cea mai buna solutie ​folosind primele j elemente ​si vedem daca elementul de pe pozitia ​i o poate imbunatati ​- $j = 1:i-1$) +      * în cazul SCMAX, presupunem ​că avem calculate $ dp[1], dp[2], ..., dp[i-1] $ și dorim să calculăm ​$ dp[i] $ (cunoaștem ​cea mai bună soluție ​folosind primele j elemente ​și vedem dacă elementul de pe poziția ​i o poate îmbunătăți ​- $j = 1:i-1$) 
-      * deoarece nu stim unde e cel mai bine sa il pune pe $v[i]$ (dupa care v[j]?​), ​incercam ​pentru toate valorile posibile ale lui j (unde $j = 1 : - 1$) +      * deoarece nu știm unde e cel mai bine să îl pune pe $v[i]$ (după care v[j]?​), ​încercăm ​pentru toate valorile posibile ale lui j (unde $j = 1 : - 1$) 
-        * **daca $v[j] < v[i] $**, atunci ​subsirul crescator ​care se termina ​pe pozitia ​j, poate fi extins la dreapta cu elementul v[i], generand ​lungimea ** dp[j] + 1 ** +        * **dacă $v[j] < v[i] $**, atunci ​subșirul crescător ​care se termină ​pe poziția ​j, poate fi extins la dreapta cu elementul v[i], generând ​lungimea ** dp[j] + 1 ** 
-           * deci dp[i] = max(dp[j] + 1), $j = 1 : i - 1$ (daca nu exista ​un astfel de j, valoarea lui max(...) este 0) +           * deci dp[i] = max(dp[j] + 1), $j = 1 : i - 1$ (dacă nu există ​un astfel de j, valoarea lui max(...) este 0) 
-        * Ce se intampla totusi daca nu exista ​un j care sa indeplineasca conditia ​de mai sus? Atunci $v[i]$ va forma singur un subsir crescator ​de lungime 1 (care poate fi la un pas ulterior) ​+        * Ce se întamplă totuși dacă nu există ​un j care să îndeplinească condiția ​de mai sus? Atunci $v[i]$ va forma singur un subșir crescător ​de lungime 1 (care poate fi folosit ​la un pas ulterior) ​
    
   Reunind cele spuse mai sus:   Reunind cele spuse mai sus:
   * $dp[1] = 1$   * $dp[1] = 1$
-  * $dp[i] = 1 + max(dp[j])$,​ unde $j = 1 : i-1$ **și** $v[j] < v[i]$+  * $dp[i] = 1 + max(dp[j])$,​ unde $j = 1 : i-1$ **și** $v[j] < v[i]$; $i=2:n$
  
-== Implementare ​recurenta ​==+== Implementare ​recurență ​==
 <spoiler Exemplu implementare>​ <spoiler Exemplu implementare>​
 +Problema se poate testa pe infoarena: [[https://​www.infoarena.ro/​problema/​scmax | Subșir crescător maximal]].
  
-Mai jos se afla un exemplu simplu de implementare a recurentei ​gasite in C++.+Mai jos se află un exemplu simplu de implementare a recurentei ​găsite în C++.
  
 <code cpp> <code cpp>
-// n   ​= ​numarul ​de elemente din vector +// n   ​= ​numărul ​de elemente din vector 
-// v   = vectorul dat (v[1], v[2], ..., v[n] - indexare de la 1 ca in explicatii)+// v   = vectorul dat (v[1], v[2], ..., v[n] - indexare de la 1 ca în explicații)
  
 void scmax(int n, vector<​int>​ &v) { void scmax(int n, vector<​int>​ &v) {
- vector<​int>​ dp(n + 1);   // ​in explicatii ​indexarea ​incepe ​de la 1+ vector<​int>​ dp(n + 1);   // ​în explicații ​indexarea ​începe ​de la 1
  
- // caz de baza + // caz de bază 
- dp[1] = 1;   // [ v[1] ] este singurul ​subsir ​(crescator) care se termina ​pe 1+ dp[1] = 1;   // [ v[1] ] este singurul ​subșir ​(crescător) care se termină ​pe 1
  
  // caz general  // caz general
  for (int i = 2; i <= n; ++i) {  for (int i = 2; i <= n; ++i) {
- dp[i] = 1;   // [ v[i] ] - este un subsir ​(crescator) care se termina ​pe i+ dp[i] = 1;   // [ v[i] ] - este un subșir ​(crescător) care se termină ​pe i
   
- // incerc sa il pun pe v[i] la finalul tuturor ​solutiilor ​disponibile + // încerc să îl pun pe v[i] la finalul tuturor ​soluțiilor ​disponibile 
- // o solutie ​se termina ​cu un element v[j]+ // o soluție ​se termină ​cu un element v[j]
  for (int j = 1; j < i; ++j) {  for (int j = 1; j < i; ++j) {
- // solutia triviala: v[i]+ // soluția trivială: v[i]
  if (v[j] < v[i]) {  if (v[j] < v[i]) {
- // din (..., v[j]) pot obtine ​(..., v[j], v[i]) + // din (..., v[j]) pot obține ​(..., v[j], v[i]) 
- // (caz in care prec[i] = j)+ // (caz în care prec[i] = j)
  
- // voi alege j-ul curent, ​cand alegerea ​imi gaseste ​solutie ​mai buna decat ce am deja+ // voi alege j-ul curent, ​când alegerea ​îmi găsește ​soluție ​mai bună decât ​ce am deja
  if (dp[j] + 1 > dp[i]) {  if (dp[j] + 1 > dp[i]) {
  dp[i] = dp[j] + 1;  dp[i] = dp[j] + 1;
Line 338: Line 353:
  }  }
  
- // solutia ​e maximul din vectorul dp+ // soluția ​e maximul din vectorul dp
  int sol = dp[1], pos = 1;  int sol = dp[1], pos = 1;
  for (int i = 2; i <= n; ++i) {  for (int i = 2; i <= n; ++i) {
Line 353: Line 368:
  
 <spoiler Exemplu implementare cu reconstituire>​ <spoiler Exemplu implementare cu reconstituire>​
 +Problema se poate testa pe infoarena: [[https://​www.infoarena.ro/​problema/​scmax | Subșir crescător maximal]].
  
-In **demo-lab03.zip** gasiti un exemplu de implementare care arata si cum puteti reconstitui SCMAX. 
-Fata de implementarea anterioara, in aceasta versiune se foloseste un tablou auxiliar prec. 
  
-$prec[i]$ = indicele j al elementului v[j], pentru care $dp[j] + 1 == dp[i]$ (adica acel j pentru care subsirul crescator ​maximal care se termina ​cu $v[i]$ este extinderea cu un element a celui care se termina ​cu $v[j]$. +În [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​demo/​lab03/​02-scmax|pa-lab::​demo/​lab03/​02-scmax]] găsiți un exemplu de implementare care arată și cum puteți reconstitui SCMAX. 
-     ​* ​daca nu exista ​un astfel de j, atunci $prec[i] = 0$ (prin conventie)+Față de implementarea anterioară,​ în această versiune se folosește un tablou auxiliar prec. 
 + 
 +$prec[i]$ = indicele j al elementului v[j], pentru care $dp[j] + 1 == dp[i]$ (adică ​acel j pentru care subșirul crescător ​maximal care se termină ​cu $v[i]$ este extinderea cu un element a celui care se termină ​cu $v[j]$. 
 +     ​* ​dacă nu există ​un astfel de j, atunci $prec[i] = 0$ (prin convenție)
 </​spoiler>​ </​spoiler>​
-=== Mentiuni ​=== +=== Mențiuni ​=== 
-Intrucat aceasta solutie ​presupune calculul iterativ (coloana ​cu coloana) a matricei dp, complexitatea este polinomiala ​(patratica ​- pentru fiecare element din tabloul, facem o trecere prin elementele deja calculate). +Întrucât această soluție ​presupune calculul iterativ (coloană ​cu coloană) a matricei dp, complexitatea este polinomială ​(pătratică ​- pentru fiecare element din tabloul, facem o trecere prin elementele deja calculate). 
-  * **complexitate ​temporala ​**: $T = O(n^2)$ +  * **complexitate ​temporală ​**: $T = O(n^2)$ 
-    * se poate obtine ​solutie in complexitate $T = O(n log n)$ daca se foloseste ​cautare binara ​pentru a gasi elementul j dorit (ex. [[https://​www.infoarena.ro/​job_detail/​1248867?​action=view-source | implemetare]]).  +    * se poate obține ​soluție în complexitate $T = O(n log n)$ dacă se folosește ​căutare binară ​pentru a găsi elementul j dorit (ex. [[https://​www.infoarena.ro/​job_detail/​1248867?​action=view-source | implemetare]]).  
-  * **complexitate ​spatiala ​** : $S = O(n)$ +  * **complexitate ​spațială ​** : $S = O(n)$ 
-    * NU putem obtine ​o complexitate ​spatiala ​mai bunaintrucat ​avem nevoie ​sa stocam ​cel putin vectorul dp (stocam si vectorul prec daca avem nevoie ​sa reconstituim SCMAX)+    * NU putem obține ​o complexitate ​spatială ​mai bunăîntrucât ​avem nevoie ​să stocăm ​cel puțin ​vectorul dp (stocăm și vectorul ​**prec** dacă avem nevoie ​să reconstituim SCMAX) 
 + 
 + 
  
 ===== Categoria 2: RUCSAC ===== ===== Categoria 2: RUCSAC =====
-Aceste ​recurente ​au o oarecare ​asemanare ​cu problema RUCSAC - varianta ​discreta ​(enunt solutie).+Aceste ​recurențe ​au o oarecare ​asemănare ​cu problema RUCSAC - varianta ​discretă ​(enunț ​soluție).
  
 ==== RUCSAC ==== ==== RUCSAC ====
  
-=== Enunt === +=== Enunț ​=== 
-Fie un set (vector) cu $ n $ obiecte (care nu pot fi taiate ​- varianta ​discreta ​a problemei). Fiecare obiect i are asociata ​o pereche ($w_i, p_i$) cu semnificatia+Fie un set (vector) cu $ n $ obiecte (care nu pot fi tăiate ​- varianta ​discretă ​a problemei). Fiecare obiect i are asociată ​o pereche ($w_i, p_i$) cu semnificația
-  * $w_i$ = $weight_i$ = greutatea obiectului cu numarul ​+  * $w_i$ = $weight_i$ = greutatea obiectului cu numărul ​
-  * $p_i$ = $price_i$ = pretul ​obiectului cu numarul ​i+  * $p_i$ = $price_i$ = prețul ​obiectului cu numărul ​i
       * $w_i >= 0$ si $p_i > 0$       * $w_i >= 0$ si $p_i > 0$
  
-Gigel are la dispozitie ​un rucsac de ** volum infinit**, dar care suporta ​o **greutate ​maxima** (notata ​cu $W$ - weight knapsack).+Gigel are la dispoziție ​un rucsac de ** volum infinit**, dar care suportă ​o **greutate ​maximă** (notată ​cu $W$ - weight knapsack).
  
-El vrea sa gaseasca ​** o submultime ​ de obiecte** pe care sa le bage in rucsac, astfel ​incat **suma profiturilor ​sa fie maxima**.+El vrea să găsească ​** o submulțime ​ de obiecte** pe care sa le bage în rucsac, astfel ​încât ​**suma profiturilor ​să fie maximă**.
  
-Daca Gigel baga in rucsac obiectul i, caracterizat de ($w_i, p_i$), atunci profitul adus de obiect este $p_i$ (presupunem ​ca il vinde cu cat valoreaza ​obiectul).+Dacă Gigel bagă în rucsac obiectul i, caracterizat de ($w_i, p_i$), atunci profitul adus de obiect este $p_i$ (presupunem ​că îl vinde cu cât valorează ​obiectul).
  
-=== Cerinta ​=== +=== Cerință ​=== 
-Sa se determine **profitul maxim** pentru Gigel.+Să se determine **profitul maxim** pentru Gigel.
  
 === Exemple === === Exemple ===
Line 395: Line 415:
 |p|6|3|2|8|5| |p|6|3|2|8|5|
  
-Raspuns: **24** (profitul maxim)+Răspuns: **24** (profitul maxim)
  
-Explicatie: va alege toate obiectele :D.+Explicație: va alege toate obiectele :D.
 </​spoiler>​ </​spoiler>​
  
 <spoiler Exemplu 2> <spoiler Exemplu 2>
-$n = 5$ si $W = 3$+$n = 5$ și $W = 3$
  
 | |1|2|3|4|5| | |1|2|3|4|5|
Line 407: Line 427:
 |p|6|3|2|8|5| |p|6|3|2|8|5|
  
-Raspuns: **13** (profitul maxim)+Răspuns: **13** (profitul maxim)
  
-Explicatie: va alege obiectele cu indicii 4 si 5 (profit: 8 + 5)+Explicație: va alege obiectele cu indicii 4 si 5 (profit: 8 + 5)
 </​spoiler>​ </​spoiler>​
  
 === Rezolvare === === Rezolvare ===
 == Tipar == == Tipar ==
-Cum am transpune tiparul de la SSM/​SCMAX ​in problema RUCSAC? +Cum am transpune tiparul de la SSM/​SCMAX ​în problema RUCSAC? 
-  * stim care este profitul maxim pe care il obtine daca folosim+  * știm care este profitul maxim pe care îl obține dacă folosim
     * doar primul element     * doar primul element
     * doar primele $2$ elemente     * doar primele $2$ elemente
     * ...     * ...
     * doar primele $i-1$ elemente     * doar primele $i-1$ elemente
-  * ajung sa ma gandesc ​la obiectul (elementul) i +  * ajung să mă gândesc ​la obiectul (elementul) i 
-    * este posibil ca acesta ​sa nu apara neaparat in solutia ​cea mai buna, caz in care nu il folosesc, deci solutia maxima ​se gaseste intre cele mentionate ​mai sus +    * este posibil ca acesta ​să nu apară neapărat în soluția ​cea mai bună, caz în care nu îl folosesc, deci soluția maximă ​se gasește între ​cele menționate ​mai sus 
-    * daca folosesc elementul i caracterizat de ($w_i, p_i$), ​in primul ​rand acesta trebuie ​sa incapa in ghiozdan...+    * dacă folosesc elementul i caracterizat de ($w_i, p_i$), ​în primul ​rând acesta trebuie ​să încapă în ghiozdan...
         * cum verific acest lucru? ​         * cum verific acest lucru? ​
-        * o recurenta ​de tipul $ dp[i] = ... $ nu va fi suficienta, pentru ​ca in aceasta problema ​am 2 dimensiuni: ** obiectele ** (submultimile ​de indici) ​si ** greutatile ​** (asociate cu obiectele / submultimile ​de obiecte).+        * o recurență ​de tipul $ dp[i] = ... $ nu va fi suficientă, pentru ​că în această problemă ​am 2 dimensiuni: ** obiectele ** (submulțimile ​de indici) ​și ** greutățile ​** (asociate cu obiectele / submulțmile ​de obiecte).
  
-== Numire ​recurenta ​== +== Numire ​recurență ​== 
-Intrucat ​la fiecare pas trebuie ​sa retinem ​** cea mai buna solutie** folosind un **prefix** din vectorul de obiecte, dar pentru ​ca trebuie ​sa punem si ** o restrictie ​de greutate** ​necesara ​(ocupata ​in rucsac), ​ ​solutia ​va fi salvata intr-un tablou auxiliar definit astfel:+Întrucât ​la fiecare pas trebuie ​să reținem ​** cea mai bună soluție** folosind un **prefix** din vectorul de obiecte, dar pentru ​că trebuie ​să punem și ** o restricție ​de greutate** ​necesară ​(ocupată ​in rucsac), ​soluția ​va fi salvată într-un tablou auxiliar definit astfel:
  
-$ dp[i][cap] $ = profitul maxim (**profit RUCSAC**) ​obtinut ​folosind (doar o parte) din primele i obiecte ​ si avand un rucsac de **capacitate ​maxima ​cap**+$ dp[i][cap] $ = profitul maxim (**profit RUCSAC**) ​obținut ​folosind (doar o parte) din primele i obiecte ​și având ​un rucsac de **capacitate ​maximă ​cap**
  
-Observatii+Observații
-  * NU exista restrictie daca in solutia mentionata ​de $dp[i][cap]$ ​este folosit OBLIGATORIU elementul i +  ​* **NU** există restricție în folosirea obiectului i în soluția menționată ​de $dp[i][cap]$ ​(a.k.a. se poate folosi sau se poate ignora). 
-  * Solutia ​problemei se gaseste in $dp[n][W]$ (profitul maxim folosind (doar o parte) din primele n elemente - adica toate; capacitatea ​maxima folosita ​este W - adica capacitatea maxima a rucsacului).+  * Soluția ​problemei se găsește în $dp[n][W]$ (profitul maxim folosind (doar o parte) din primele n elemente - adică soluția bazată pe inspectarea tuturor obiectelor; capacitatea ​maximă folosită ​este W - adică soluția bazată pe ghiozdanul de capacitate maximă).
  
-== Gasire recurenta ​== +== Găsire recurență ​== 
-  * **Cazul de baza** +  * **Cazul de bază** 
-    * Daca avem o submultime vida de obiecte selectate.+    * Dacă avem o submulțime vidă de obiecte selectate.
       * $ dp[0][cap] = 0 $       * $ dp[0][cap] = 0 $
-      * ExplicatieDaca nu alegem obiecte, atunci profitul este 0 indiferent de capacitate.+      * ExplicațieDacă nu alegem obiecte, atunci profitul este 0 indiferent de capacitate.
  
   * ** Cazul general **   * ** Cazul general **
     * $ dp[i][cap] = ? $     * $ dp[i][cap] = ? $
-    * presupune inductiv ​ca avem rezolvate toate subproblemele mai mici +    * presupune inductiv ​că avem rezolvate toate subproblemele mai mici 
-      * subprobleme mai mici inseamna sa foloseasca ​mai putine ​obiecte sau un rucsac cu capacitatea mai mica +      * subprobleme mai mici înseamnă să folosească ​mai puține ​obiecte sau un rucsac cu capacitatea mai mică 
-      * vedem daca prin folosirea obiectului i, obtinem ​cea mai buna solutie ​in $dp[i][cap]$+      * vedem dacă prin folosirea obiectului i, obținem ​cea mai bună soluție ​in $dp[i][cap]$
         * **NU folosesc obiectul i**         * **NU folosesc obiectul i**
-          * in acest caz, o sa alegem cea mai buna solutie formata ​cu celelalte $i-1$ elemente ​si aceeasi ​capacitate a rucsacului +          * în acest caz, o să alegem cea mai bună soluție formată ​cu celelalte $i-1$ elemente ​și aceeași ​capacitate a rucsacului 
-          * solutia generata ​de acest caz: $dp[i][cap] = dp[i - 1][cap]$+          * soluția generată ​de acest caz: $dp[i][cap] = dp[i - 1][cap]$
         * **folosesc obiectul i**         * **folosesc obiectul i**
-          * daca il folosesc, ​inseamna ca pentru el trebuie ​sa am rezervata in rucsac o capacitate ​egala cu $w_i$ +          * dacă îl folosesc, ​înseamnă că pentru el trebuie ​să am rezervată în rucsac o capacitate ​egală ​cu $w_i$ 
-            * adica cand am selectat dintre primele $i-1$ elemente, nu trebuia ​sa ocup mai mult de $cap - w_i$ din capacitatea rucsacului +            * adică când am selectat dintre primele $i-1$ elemente, nu trebuia ​să ocup mai mult de $cap - w_i$ din capacitatea rucsacului 
-            * fata de subproblema ​mentionatacastig in plus $p_i$ (profitul pe care il aduce acest obiect +            * față ​de subproblema ​menționatăcâștig în plus $p_i$ (profitul pe care îl aduce acest obiect 
-          * solutia generata ​de acest caz: $dp[i][cap] = dp[i - 1][cap - w_i] + p_i$ +          * soluția generată ​de acest caz: $dp[i][cap] = dp[i - 1][cap - w_i] + p_i$ 
   ​   ​
-   ​Reunind cele spuse mai sus, obtinem+   ​Reunind cele spuse mai sus, obținem
-   * $dp[0][cap] = 0$, pentru $cap = 0 : G+   * $dp[0][cap] = 0$, pentru $cap = 0 : W
-   * $dp[i][cap] = max(dp[i - 1]cap], dp[i - 1][cap - w_i] + p_i)$+   * $dp[i][cap] = max(dp[i - 1][cap], dp[i - 1][cap - w_i] + p_i)$
      * pentru $i = 1: n$, $cap = 0:W$       * pentru $i = 1: n$, $cap = 0:W$ 
  
-== Implementare ​recurenta ​==+== Implementare ​recurență ​==
 <spoiler Exemplu implementare>​ <spoiler Exemplu implementare>​
-Mai jos se afla un exemplu simplu de implementare a recurentei gasite ​in C++.+Problema se poate testa pe infoarena:​[[https://​www.infoarena.ro/​problema/​rucsac | Problema rucsacului]]. 
 + 
 +Mai jos se află un exemplu simplu de implementare a recurenței găsite ​in C++.
  
 <code cpp> <code cpp>
  
-// n   ​= ​numarul ​de obiecte din colectie +// n   ​= ​numărul ​de obiecte din colecție 
-// W   = capacitatea ​maxima ​a rucsacului+// W   = capacitatea ​maximă ​a rucsacului
 // (w[i], p[i]) = caracteristicile obiectului i ($i = 1 : n) // (w[i], p[i]) = caracteristicile obiectului i ($i = 1 : n)
  
 int rucsac(int n, int W, vector<​int>​ &w, vector<​int>​ &p) { int rucsac(int n, int W, vector<​int>​ &w, vector<​int>​ &p) {
     // dp este o matrice de dimensiune (n + 1) x (W + 1)     // dp este o matrice de dimensiune (n + 1) x (W + 1)
-    // pentru ​ca folosim dp[0][*] pentru ​multimea vida +    // pentru ​că folosim dp[0][*] pentru ​mulțimea vidă 
-    //                   ​dp[*][0] pentru ​situatia in care ghiozdanul are capacitate 0 +    //                   ​dp[*][0] pentru ​situația în care ghiozdanul are capacitate 0 
-    vector< vector<​int>​ > dp(n + 1); +    vector< vector<​int>​ > dp(n + 1, vector<int>(W + 1, 0));
-    for (int i = 0; i <= n; ++i) { +
-        dp[i].resize(W + 1); +
-    }+
  
-    // cazul de baza+    // cazul de bază
     for (int cap = 0; cap <= W; ++cap) {     for (int cap = 0; cap <= W; ++cap) {
         dp[0][cap] = 0;         dp[0][cap] = 0;
Line 487: Line 506:
     for (int i = 1; i <= n; ++i) {     for (int i = 1; i <= n; ++i) {
         for (int cap = 0; cap <= W; ++cap) {         for (int cap = 0; cap <= W; ++cap) {
-            // nu folosesc ​obiectu ​i => e solutia ​de la pasul i - 1+            // nu folosesc ​obiectul ​i => e soluția ​de la pasul i - 1
             dp[i][cap] = dp[i-1][cap];​             dp[i][cap] = dp[i-1][cap];​
  
-            // folosesc obiectul i, deci trebuie ​sa rezerv w[i] unitati in rucsac +            // folosesc obiectul i, deci trebuie ​să rezerv w[i] unități în rucsac 
-            // inseamna ​ca inainte ​trebuie ​sa ocup maxim cap - w[i] unitati+            // înseamnă ​ca înainte ​trebuie ​să ocup maxim cap - w[i] unități
             if (cap - w[i] >= 0) {             if (cap - w[i] >= 0) {
                 int sol_aux = dp[i-1][cap - w[i]] + p[i];                 int sol_aux = dp[i-1][cap - w[i]] + p[i];
Line 506: Line 525:
 </​spoiler>​ </​spoiler>​
  
-=== Mentiuni ​=== +=== Mențiuni ​=== 
-Intrucat aceasta solutie ​presupune calculul iterativ (linie cu linie) a matricei dp, complexitatea este polinomiala.   +Întrucât această soluție ​presupune calculul iterativ (linie cu linie) a matricei dp, complexitatea este polinomială.   
-  * **complexitate ​temporala ​**: $T = O(n * W)$ +  * **complexitate ​temporală ​**: $T = O(n * W)$ 
-  * **complexitate ​spatiala ​** : $S = O(n * W)$ +  * **complexitate ​spațială ​** : $S = O(n * W)$ 
-        * daca nu ne intereseaza sa reconstituim ​solutia ​(sa afisam submultimea ​efectiv), atunci putem sa NU stocam toata matricea dp +        * dacă nu ne interesează să reconstituim ​soluția ​(să afișăm submulțimea ​efectiv), atunci putem să NU stocăm toată ​matricea dp 
-        * ca sa calculam ​o linie, avem nevoie doar de ultima linie +        * ca să calculăm ​o linie, avem nevoie doar de ultima linie 
-        * putem sa stocam ​la orice moment de timp doar ultima linie si linia curenta +        * putem să stocăm ​la orice moment de timp doar ultima linie și linia curentă 
-        * complexitatea ​spatiala ​se reduce astfel la $S = O(W)$  +        * complexitatea ​spațială ​se reduce astfel la $S = O(W)$  
-      + 
 ===== Exercitii ===== ===== Exercitii =====
 <​note>​ <​note>​
-In laboratorul de astazi, implementarea exercitiilor nu va fi punctata. Cu toate acestea, daca doriti sa implementati problemele propuse spre rezolvare, puteti folosi scheletul ​de laborator ​din arhiva {{pa:new_pa:skel-lab03.zip}}.+Scheletul ​de laborator ​se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab03|pa-lab::skel/lab03]].
 </​note>​ </​note>​
  
Line 585: Line 605:
 </​spoiler>​ </​spoiler>​
  
 +<​hidden>​
 <spoiler Solutie> <spoiler Solutie>
 Problema preluata de [[https://​leetcode.com/​problems/​coin-change/​description/​|aici]]. Solutia este [[https://​leetcode.com/​problems/​coin-change/​solution/​ | aici]]. Problema preluata de [[https://​leetcode.com/​problems/​coin-change/​description/​|aici]]. Solutia este [[https://​leetcode.com/​problems/​coin-change/​solution/​ | aici]].
 </​spoiler>​ </​spoiler>​
 +</​hidden>​
  
 === 2. CMLSC === === 2. CMLSC ===
Line 677: Line 699:
  
  
 +<​hidden>​
 <spoiler Solutie> <spoiler Solutie>
 Problema preluata de [[https://​infoarena.ro/​problema/​cmlsc|aici]]. Problema preluata de [[https://​infoarena.ro/​problema/​cmlsc|aici]].
Line 688: Line 710:
 Complexitate:​ $O(n * m)$. Complexitate:​ $O(n * m)$.
 </​spoiler>​ </​spoiler>​
 +</​hidden>​
 === BONUS === === BONUS ===
 <spoiler Custi> <spoiler Custi>
Line 735: Line 757:
 ===== Referințe ===== ===== Referințe =====
  
-[0] Capitolul ​**Dynamic Programming** ​din **Introductions ​to Algorithms** de către T. H. Cormen, ​C. E. Leiserson, ​R. L. Rivest, C. Stein+[0] Chapter ​**Dynamic Programming**, “Introduction ​to Algorithms”, Thomas ​H. Cormen, ​Charles ​E. Leiserson, ​Ronald ​L. Rivest ​and Clifford ​Stein
  
 [1] [[http://​infoarena.ro/​problema/​ssm]] [1] [[http://​infoarena.ro/​problema/​ssm]]
Line 742: Line 764:
  
 [3] [[http://​infoarena.ro/​problema/​rucsac]] [3] [[http://​infoarena.ro/​problema/​rucsac]]
- 
-[4] [[https://​www.geeksforgeeks.org/​dynamic-programming/​|Colecție de probleme de programare dinamică (geeksforgeeks.com)]] 
- 
-[5] [[https://​practice.geeksforgeeks.org/​explore/?​category%5B%5D=Dynamic%20Programming&​page=1&​sortBy=accuracy|Probleme de interviu care se rezolvă cu PD]]  
- 
-[6] Sniedovich, Moshe. Dynamic programming:​ foundations and principles. CRC press, 2010. 
- 
  
pa/laboratoare/laborator-03.txt · Last modified: 2023/03/15 16:54 by radu.nichita
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