Differences

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

Link to this comparison view

pa:laboratoare:laborator-08 [2019/04/16 01:19]
mihaela.balint [Puncte de articulatie]
pa:laboratoare:laborator-08 [2024/04/23 23:13] (current)
radu.nichita [Obiective laborator]
Line 1: Line 1:
-====== Laborator 08: Aplicatii DFS ======+====== Laborator 08: Drumuri minime în grafuri: sursă / destinație unică. (1/2) ======
  
-===== Obiective laborator ===== 
-Întelegerea noţiunilor teoretice: 
  
-  * tare conexitatecomponente tare conexe (pentru grafuri orientate) +{{:​pa:​new_pa:​partners:​adobe-logo.png?​155 |}} 
-  * punct de articulaţie (pentru grafuri neorientate) +\\ \\ \\ Changing the world through digital experiences is what Adobe’s all about. We give everyone - from emerging artists to global brands - everything they need to design and deliver exceptional digital experiences! We’re passionate about empowering people to create beautiful and powerful imagesvideos, and appsand transform how companies interact with customers across every screen.
-  * punţi (pentru grafuri neorientate) +
-  * componente biconexe (în generalpentru grafuri neorientate)+
  
-Înţelegerea algoritmilor ce rezolvă aceste probleme şi implementarea acestor algoritmi.  +===== Obiective laborator ​=====
-===== Importanţă – aplicaţii practice ​=====+
  
-  ​*Componentele biconexe au aplicaţii importante în reţelistică,​ deoarece o componentă biconexă asigură redundanţa. +În laboratorul 8 vom introduce contextul pentru **Shortest-paths problem** și vom studia **Single-source shortest-paths problem**, iar în laboratorul 9 vom continua cu **All-pairs shortest-paths problem**.
-  ​*Descompunerea în componente tare conexe: data miningcompilatoare,​ calcul ştiinţific,​ 2SAT +
-===== Notiuni teoretice =====+
  
-    ​***Tare conexitate.** Un graf orientat este tare conex, dacă oricare ar fi două vârfuri u şi v, ele sunt tare conectate ​(strongly connected- există drum atât de la u la v, cât şi de la v la u.+  ​Înțelegerea conceptelor de cost asociat unei muchii, relaxare a unei muchii. 
 +  ​Prezentarea problemei drumului de cost minim (diverse variante)
 +  * Prezentarea algoritmilor pentru calculul drumurilor minime.
  
-    *O componentă tare conexă este un subgraf maximal tare conex al unui graf orientat, adică o submulţime de vârfuri U din V, astfel încât pentru orice u şi v din U ele sunt tare conectate. Dacă fiecare componentă tare conexă este redusă într-un singur nod, se va obţine **un graf orientat aciclic**.+===== Shortest-paths problem: single-source/​destination =====
  
-De exemplu:+Vă rugăm să parcugeți [[https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​shortest-paths-problem|Shortest-paths problem]] pentru a vă familiariza cu contextul, problema și notațiile folosite.
  
-{{ pa:laboratoare:​7.1.png?​400 |}} +Concepte necesare:
-  +
-    ***Un punct de articulaţie (cut vertex)** este un nod al unui graf a cărui eliminare duce la creşterea numărului de componente conexe ale acelui graf.+
  
-    ​***O punte (bridge)** este o muchie a unui graf (se mai numeşte şi **muchie critică**) a cărei eliminare duce la creşterea numărului de componente conexe ale acelui graf.+  ​* **cost muchie** **edge cost** 
 +  * **cost drum** / **path cost** 
 +  * **problema drumurilor minime: sursă / destinație unică** / **single-source/​destination shortest-paths problem** 
 +  * **relaxarea unei muchii** / **edge relaxation** 
 +  * **reconstruirea unui drum** / **RebuildPath**
  
-{{ pa:​laboratoare:​7.2.png?​400 |}}+===== Algoritmi =====
  
-    ​***Biconexitate**. Un graf biconex este un graf conex cu proprietatea că eliminând oricare nod al acestuiagraful rămâne conex. ​+În acest laborator vom studia ​**single-source/​destination shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:
  
-    ​*O componentă biconexă a unui graf este o mulţime ​**maximală** de noduri care respectă proprietatea de biconexitate+  ​* **Dijkstra**: presupune ca toate costurile din graf sunt nenegative
-  +  * **Bellman-Ford**permite costuri negative în graf, dar presupune că nu există cicluri de costuri negative.
-{{ pa:laboratoare:​7.3.png?400 |}}+
  
-===== Componente tare conexe =====+Vom prezenta fiecare algoritm, îl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare.
  
-Vom porni de la definiţie pentru a afla componenta tare conexă ​din care face parte un nod v. Vom parcurge graful (DFS sau BFS) pentru ​a găsi o mulţime de noduri S ce sunt accesibile din v. Vom parcurge apoi graful transpus (obţinut prin inversarea arcelor din graful iniţial), determinând o nouă mulţime de noduri T ce sunt accesibile din v în graful transpus. Intersecţia dintre S şi T va reprezenta componenta tare conexa. Graful iniţial şi cel transpus au aceleaşi componente conexe.+Puteți consulta capitolul **Single-Source Shortest Paths** ​din **Introduction to Algorithms** [0] pentru ​mai multe detalii despre acești algoritmi.
  
-==== Algoritmul lui Kosaraju ​====+===== Dijkstra =====
  
-Algoritmul ​foloseşte două DFS (una pe graful iniţial şi una pe graful transpusşi o stivă pentru a reţine ordinea terminării parcurgerii nodurilor grafului original ​(evitând astfel o sortare a nodurilor după acest timp la terminarea parcurgerii).+Algoritmul ​lui [[https://​en.wikipedia.org/​wiki/​Edsger_W._Dijkstra|Edsger Wybe **Dijkstra**]] ​(**Dijkstra’s algorithm**rezolvă **shortest-paths problem** în grafuri **G = (V, E)** cu costurile muchiilor **nenegative** ($w[u][v] \ge 0$). 
 + 
 +==== Dijsktra - Pseudocod ====
  
 <code cpp> <code cpp>
-ctc(G = (VE)+// apply Dijkstra'​s algorithm from source 
- S <- stiva vida +// 
- culoare[1..n] ​alb +// source ​   = the source for the computing distances 
- cat timp exista un nod v din V care nu e pe stiva +// nodes     = list of all nodes from G 
- dfs(G, v+// adj[node] = the adjacency list of node 
- culoare[1..n] = alb +//             ​example:​ adj[node] = {..., neigh, ...} => edge (nodeneigh
- cat timp S !stiva vida +// 
- v = pop(S+// returns: d, p 
- dfsT(GT, v) /* toate nodurile ce pot fi vizitate din v fac  parte din ctcdupa vizitare, aceastea sunt scoase din S si din G */+//          d distance vector 
 +//          p = parent vector 
 +// 
 +Dijsktra(source, ​G=(nodesadj){ 
 +  // STEP 0: initialize results 
 +  // d[node] = distance from source to node 
 +  // p[node] ​parent of node on the shortest path from source to node 
 +  ​foreach ​(node in nodes{ 
 +      ​d[node] = +oo;                          ​// distance not yet computed 
 +      p[node] = null                        ​// parent not yet found 
 +  }
  
-dfs(G, v) +  ​// STEP 1: initialize a priority queue 
-        culoare[v] = gri +  ​pq ​{};
-        pentru fiecare (v, u) din E +
-        daca culoare[u] == alb +
-                dfs(u) +
-        push(S, v) // nodul este terminat de expandat, este pus pe stiva +
-        ​culoare[v] ​negru+
  
-dfsT(G, v– similar cu dfs(Gv): fara stiva, dar cu retinerea solutiei +  // STEP 2: add the source(sinto q 
-</​code>​+  d[source] = 0;                              // distance from source to source 
 +  p[source] = null;                           // source never has parent 
 +  pq.push(sourced[source]);
  
-ComplexitateO(|V| + |E|+  // STEP 3start relaxation loop using the node(sfrom pq 
-  +  while (!pq.empty()) ​{ 
-{{ pa:laboratoare:​7.4.png?400 |}}+    // STEP 3.1extract the next node (having the minimum estimate distance) 
 +    node = pq.pop_min();
  
-==== Algoritmul lui Tarjan ====+    // [optional] STEP 3.2: print/use the node
  
-Algoritmul foloseşte o singură parcurgere DFS şi o stivăIdeea de bază a algoritmului este că o parcurgere în adâncime porneşte dintr-un nod de start. Componentele tare conexe formează subarborii arborelui de căutaredăcinile cărora sunt de asemenea rădăcini pentru componentele tare conexe.+    // STEP 3.3: relax all edges (nodeneigh) 
 +    foreach (neigh in adj[node]) { 
 +        if (d[node] + w[node][neigh] < d[neigh]) {        // try to relax edge (node, neigh) 
 +          d[neigh] = d[node] + w[node][neigh]; ​           // update the new distance from source to neigh 
 +          p[neigh] = node;                                // save parent
  
-Nodurile sunt puse pe o stivă, în ordinea vizităriiCând parcurgerea termină de vizitat un subarborenodurile sunt scoase din stivă şi se determină pentru fiecare nod dacă este rădăcina unei component tare conexe. Dacă un nod este rădăcina unei componente, atunci el şi toate nodurile scoase din stivă înaintea lui formează acea componenta tare conexă.+          pq.push( (neigh, d[neigh]) );                     // replace distance for neigh in pq 
 +        } 
 +    } 
 +  }
  
-Pentru a determina daca un nod este radacina unei componente conexecalculam pentru fiecare nod:+  return dp; 
 +}
  
-<code cpp> +// Usage example: 
-idx[v] ​    -> nivelul ​ordinea de vizitare +d, p = Dijkstra(source,​ G=(nodes, edges)); 
-lowlink[v-> min { idx[u] | u este accesibil din v }+// 1. Use distances from d 
 +// (e.g. d[node= distance from source to node) 
 +// 
 +// 2. Rebuild path from node to source using parents (p) 
 +RebuildPath(source,​ destination,​ p);
 </​code>​ </​code>​
 +Acest algoritm menține un set de noduri (de exemplu, un min heap / priority queue) cu distanța minimă față de sursă fiind estimată. Cât timp mai există noduri pentru care distanța minimă față de sursă nu este finalizată,​ se extrage nodul $node$ pentru care estimarea este minimă și această se consideră finalizată. Deoarece acum distanța de la nodul sursă la $u$ este finală, se relaxează toate muchiile care pornesc din $node$, care sunt de forma $(node, neigh)$.
  
-**este rădăcina unei componente tare conexe <=> lowlink[v] = idx[v].**+Pentru ca acest algoritm alege mereu “cel mai apropiat nod de sursă” spunem că **Algoritmul lui Dijsktra** este de tip greedyDemonstrația se poate găsi în **Introduction to algorithms**.
  
-<code cpp> +==== Exemple ====
-ctc_tarjan(G ​(V, E))  +
-        index +
-        S stiva vida +
-        pentru fiecare v din V +
-                daca (idx[v] nu e definit) // nu a fost vizitat +
-                        tarjan(G, v)+
  
-tarjan(G, v)       +=== Exemplu Dijkstra ​===
-        idx[v] ​index +
-        lowlink[v] ​index  +
-        index index + 1  +
-        push(S, v)  +
-         +
-        pentru (v, u) din E  +
-                daca (idx[u] nu e definit)  +
-                        tarjan(G, u)  +
-                        lowlink[v] ​min(lowlink[v],​ lowlink[u])  +
-                altfel +
-                        daca (u e in S)  +
-                                lowlink[v] ​min(lowlink[v],​ idx[u])  +
-         +
-        daca (lowlink[v] ​== idx[v])  +
-                // este v radacina unei CTC?  +
-                print "O noua CTC: "  +
-                repeat  +
-                        u = pop(S)  +
-                        print u  +
-                until (u == v) +
-</​code>​+
  
-ComplexitateO(|V| + |E|)+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-example.png?​512Exemplu Dijkstra}}
  
-{{ pa:laboratoare:​7.5.png?​400 |}} +Drumurile minime calculate de algoritmul lui Dijkstra sunt:
-  +
-===== Puncte de articulatie =====+
  
-Pentru determinarea punctelor de articulaţie într-un graf neorientat se foloseşte o parcurgere în adâncime modificată,​ reţinându-se informaţii suplimentare pentru fiecare nod. Acest algoritm a fost identificat tot de către Tarjan si este foarte similar cu algoritmul pentru determinarea CTC în grafuri orientate prezentat anterior. +|node|1|2|3|4|5|6|7|8|9| \\ 
-Fie T un arbore de adâncime descoperit de parcurgerea grafului. Atunci, un nod v este punct de articulaţie dacă: ​+|d[node]|0|1|1|3|3|1|3|4|$+∞$| \\ 
 +|p[node]|null|1|1|3|4|1|6|5|null| \\
  
-  *v este rădăcina lui T şi v are doi sau mai mulţi copii  
  
-sau +<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:​
  
-  *v nu este rădăcina lui T şare un copil u în Tastfel încât nici un nod din subarborele dominat ​de u nu este conectat ​cu un strămoş al lui v printr-o muchie înapoi ​(copii lui nu pot ajunge pe altă cale pe un nivel superior în arborele ​de adâncime). +  * ''​%%n = 9%%'',​ ''​%%m = 10%%''​ 
 +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
 +  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
 +  * Alegem $source = 1$ și rulăm pas cu pas algoritmul. 
 +  * STEP 0: 
 +    * $d[node] = +∞$, pentru $node = 1:9$ 
 +    * $p[node] = null$, pentru $node = 1:9$ 
 +  * STEP 1: 
 +    * $pq = \{\}$ (coadă de priorități în care vom băga elemente $(noded[node])$, deci vom ordona nodurilor **crescător după distanța minimă față de sursă** cunoscută în acel moment). 
 +  * STEP 2: initializări pentru sursă 
 +    * $d[1] = 0$ 
 +    * $p[1] = null$ 
 +    * $pq.push( (1, 0) )$ (inițial avem în coadă doar sura **1** cu distanța **0**). 
 +  * STEP 3: scoate câte un nod la fiecare pas și relaxăm toate muchiile care pornesc ​din acesta; ne oprim când coada e goală. 
 +    * $pq = \{ (1, 0) \}$ => $pq.{pop\_min}()$ scoate $node = 1$. Relaxăm muchiile care pornesc din **1**. 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 + 1 < +∞$). **DA**, atunci **relaxarea muchiei are loc**: 
 +        * $d[2] = d[1] + w[1][2] = 0 + 1 = 1$ (actualizăm distanța) 
 +        * $p[2] = 1$ (actualizăm părintele ​de pe drum) 
 +        * $pq.push( (2, 1) )$ (adăugăm nodul cu noua distanță, pentru a putea fi folosit la un pas ulterior) 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 + 1 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[3] = 0 + 1 = 1$ 
 +        * $p[3] = 1$ 
 +        * $pq.push( (3, 1) )$ 
 +      * $(1, 6)$: Verificăm dacă $d[1] + w[1][6] < d[6]$ (adică $0 + 1 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[6] = 0 + 1 = 1$ 
 +        * $p[6] = 1$ 
 +        * $pq.push( (6, 1) )$ 
 +    * $pq = \{ (2, 1); (3, 1); (6, 1); \}$ => $pq.{pop\_min}()$ scoate $node = 2$. Relaxăm muchiile care pornesc din **2**. 
 +      * $(2, 8)$: Verificăm dacă $d[2] + w[2][8] < d[8]$ (adică $1 + 10 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[8] = 1 + 10 = 11$ 
 +        * $p[8] = 2$ 
 +        * $pq.push( (8, 11) )$ 
 +    * $pq = \{ (3, 1); (6, 1); (8, 11); \}$ => $pq.{pop\_min}()$ scoate $node = 3$. Relaxăm muchiile care pornesc din **3**. 
 +      * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $1 + 2 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[4] = 1 + 2 = 3$ 
 +        * $p[4] = 3$ 
 +        * $pq.push( (4, 3) )$ 
 +    * $pq = \{ (6, 1); (4, 3) (8, 11); \}$ => $pq.{pop\_min}()$ scoate $node = 6$. Relaxăm muchiile care pornesc din **6**. 
 +      * $(6, 7)$: Verificăm dacă $d[6] + w[6][7] < d[7]$ (adică $1 + 2 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[7] = 1 + 2 = 3$ 
 +        * $p[7] = 6$ 
 +        * $pq.push( (7, 3) )$ 
 +    * $pq = \{ (4, 3); (7, 3) (8, 11); \}$ => $pq.{pop\_min}()$ scoate $node = 4$. Relaxăm muchiile care pornesc din **4**. 
 +      * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[5]$ (adică $3 + 0 < +∞$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[5] = 3 + 0 = 3$ 
 +        * $p[5] = 4$ 
 +        * $pq.push( (5, 3) )$ 
 +    * $pq = \{ (5, 3); (7, 3) (8, 11); \}$ => $pq.{pop\_min}()$ scoate $node = 5$. Relaxăm muchiile care pornesc din **5**. 
 +      * $(5, 8)$: Verificăm dacă $d[5] + w[5][8] < d[8]$ (adică $3 + 1 < 11$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[8] = 3 + 1 = 4$ 
 +        * $p[8] = 5$ 
 +        * $pq.push( (8, 4) )$ (înlocuim nodul 8 din coadă ținînd cont de noua distanță!) 
 +    * $pq = \{ (7, 3) (8, 4); \}$ => $pq.{pop\_min}()$ scoate $node = 7$. Relaxăm muchiile care pornesc din **7**. 
 +      * $(7, 8)$: Verificăm dacă $d[7] + w[7][8] < d[8]$ (adică $3 + 2 < 4$). **NU**, atunci relaxarea muchiei **NU** are loc (nu se trece la următorul pas). 
 +    * $pq = \{ (8, 4); \}$ => $pq.{pop\_min}()$ scoate $node = 8$. Nu există muchii care pornesc din **8**, nu avem ce relaxa la acest pas. 
 +    * $pq = \{ \}$ => Coada este goală. STOP! 
 +  * Drumurile minime sunt finale (cele menționate anterior).
  
-Găsirea punctelor care se încadrează în primul caz este uşor de realizat.+Observații:​
  
-Notăm: +  * Distanța minimă față de sursă pentru un nod poate fi recalculată de mai multe ori (de exemplunodul **8**). De fiecare dată nodul se reintroduce în coadă (împreună cu noua distanță). Pentru eficiență,​ înlocuim elementul curent, deoarece are asociată ​distanță mai mare și nu ar mai fi folosită. 
-<code cpp> +  * Mereu se extrage nodul cel mai apropiat ​de sursă. Distanța asociată cu el în acel moment este finală. În acest exemplu nodurile sunt scoase în ordinea: $1, 2, 3, 6, 4, 5, 7, 8$. 
-idx[u] = timpul ​de descoperire a nodului u  +  * Nodul **9** nu este accesibil, datele pentru el rămân ca la inițializare.
-low[u] = min{idx[u]} U { idx[v] : (uveste muchie înapoi } U  +
-        { low[vi] : vi copil al lui u în arborele ​de adâncime} ) +
-</​code>​+
  
-** v este punct de articulaţie ​<=low[u] ≥ idx[v], pentru cel puțin un copil u al lui v în T. **+</spoiler\\
  
-<code cpp> 
-puncte_articulatie(G = (V, E)) 
-        timp = 0 
-        pentru fiecare v din V 
-        daca (idx[v] nu e definit) 
-                dfsCV(G, v) 
  
-dfsCV(G, v) +=== [Studiu de cazExemplu Dijkstra: costuri negativerezultate eronate ​===
-        idx[v] ​timp +
-        low[v] ​timp +
-        timp timp + 1 +
-        copii = { } // multime vida +
-        pentru fiecare (v, u) din E +
-                daca (idx[unu e definit) +
-                        // inseamna ca nodul u este nedescoperitdeci alb +
-                        copii copii U {u} +
-                        dfsCV(G, u) +
-                        low[v] ​min(low[v], low[u]) +
-                altfel +
-                        // inseamna ca nodul u este descoperit, deci gri, iar muchia v->u este muchie inapoi +
-                        low[v] ​min(low[v], idx[u]) +
-                         +
-        daca v radacina arborelui +
-                daca |copii| >= 2 +
-                        v este punct de articulatie +
-        altfel +
-                daca (∃u ∈ copii) astfel incat (low[u] >= idx[v]) +
-                        v este punct de articulatie +
-</​code>​+
  
-Complexitate:​ O(|V| + |E|)+Un nod scos din coadă în algoritmul lui Dijkstra are distanță calculată și finală. Ca această abordare greedy să fie corectă, trebuie ca toate costurile din graf să fie **nenegative**.
  
-{{ pa:​laboratoare:​7.6.png?​400 |}}  +<spoiler Exemplu rezultate eronate aplicare Dijkstra pe graf cu costuri negative>​
-===== Punti =====+
  
-Pentru ​a determina muchiile critice se foloseşte tot o parcurgere în adâncime modificatăpornind de la următoarea observaţie:​ **muchiile critice sunt muchiile care nu apar în niciun ciclu.** Prin urmare, o muchie de întoarcere nu poate fi critică, deoarece o astfel de muchie închide întotdeauna un ciclu. Trebuie să verificăm pentru muchiile de avansare (în număr de |V| - 1) dacă fac parte dintr-un ciclu. Să considerăm că dorim să verificăm muchia de avansare (v, u).+Vom analiza 2 exemple. ​Pentru ​simplitatepresupunem ca $source = 1$.
  
-Ne vom folosi ​de low[v(definit ​la punctul ​anterior): ​dacă din nodul u putem să ajungem ​pe un nivel mai mic sau egal cu nivelul lui v, atunci muchia ​nu este criticăîn caz contrar ea este critică.+În figura următoare avem o topologie simplă, pentru care se întâmplă ca algortimul Dijkstra sa producă vectorul ​de distanțe corect. 
 + 
 +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example01.png?​512| Dijkstra cu costuri negative - exemplu 01}} 
 + 
 +Dacă complicăm puțin topologia, de exemplu să adăugăm muchii astfel încât să forțăm actualizarea distanței de la sursă la sursă, obținem următoarea figură: 
 + 
 +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example02.png?​512| Dijkstra cu costuri negative - exemplu 02}} 
 + 
 +Când nodul **5** este scos din coadă la un moment dat, acesta va relaxa muschi $(5, 1)$ (pentru că $d[5+ w[5][1] = 1 - 2 = -1 < d[1]$). Acest lucru duce la $d[1] = -1$, adică reactualizarea distanței pentru un nod, care anterior ​a fost scos din coadă, deci distanța calculată era finală. Deci ajunge la o contradicție. 
 + 
 +</​spoiler>​ \\ 
 + 
 + 
 +==== Complexitate ==== 
 + 
 +  * **complexitate temporală**:​ $T = O(m * log n)\ sau\ O(|E| * log |V|)$ 
 +  * **complexitate spațială** ​$S = O(n)$ 
 + 
 +<spoiler Detalii (analiză + optimizări)>​ 
 + 
 +  * **complexitate temporală**:​ Se încearcă relaxarea tuturor celor **m** muchii ​din graf, care poate presupune o inserare în set / coada de priorități - $O(log n)$, deci $O(m * log n)$ în total pentru relaxări. În plus, se fac $n$ ștergeri, adică $O(n * log n)$, însă acest termen se poate neglija (presupunem că $n << m$). 
 +  * **complexitate spațială** : Se ține o coadă de priorități / un set cum maximum **n** noduri. 
 +  * **optimizare**:​ După cum se poate vedea, complexitatea este dată de numărul de relaxări ($m$ - de care nu putem scăpa, deoarece vrem să relaxăm toate muchiile ca să fim siguri că am găsit drumurile de lungime minimă) și de complexitatea operațiilor de ștergere / căutare / inserare în structura de date de tip priority queue. Putem încerca mai multe tipuri de heapuri - [[https://​en.wikipedia.org/​wiki/​Heap_(data_structure)#​Comparison_of_theoretic_bounds_for_variants|Heap:​ Comparison of theoretic bounds for variants]]. 
 +    * **Dijkstra cu heap binar** - $O(m log n)$: Soluția bazată ​pe un priority queue clasic de mai sus. 
 +    * **Dijkstra ​cu heap Fibonacci** - $O(n logn + m)$: Cea mai rapidă implementare care se poate obține. Exemple în [[https://​kbaile03.github.io/​projects/​fibo_dijk/​fibo_dijk.html|Fibonacci Heaps and Dijkstra’s Algorithm - A Visualization]]. 
 + 
 +</​spoiler>​ \\ 
 + 
 + 
 +===== Bellman-Ford ===== 
 + 
 +Algoritmul Bellman-Ford a fost inițial propus de Alfonso Shimbel (1955), dar publicat (1956-1958) și numit ulterior după [[https://​en.wikipedia.org/​wiki/​Richard_E._Bellman|Richard E. **Bellman**]] și [[https://​en.wikipedia.org/​wiki/​L._R._Ford_Jr.|Lester Randolph **Ford** Jr. ]]. Acest algoritm rezolvă **shortest-paths problem** în grafuri **G = (V, E)** cu costurile muchiilor oarecare ($w[u][v]$ poate fi și negativ)dar **fără** cicluri de cost negativ (în grafurile de cost negativ ​nu putem defini costul unui drum care conține un ciclu). 
 + 
 +Algoritmul **detectează** prezența ciclurilor de costuri negative ​în graf. Dacă nu există cicluri de cost negativ în graf, acesta furnizează vectorul de distanțe $d$ și vectorul de părinți $p$ (ca și Dijkstra). 
 + 
 +==== Bellman-Ford - Pseudocod ====
  
 <code cpp> <code cpp>
-dfsB(G, vparinte+// apply Bellman-Ford'​s algorithm from source 
- idx[v] = timp +// 
- low[v] = timp +// source ​   = the source for the computing distances 
- timp timp 1 +// nodes     = list of all nodes from G 
- pentru fiecare ​(v, udin E +// edges     = list of all edges from G 
- daca u nu este parintele lui v +// 
- daca idx[unu este definit +// returns: has_cycled
- dfsB(G, uv+//          has_cycle = negative cycle detection flag (true if found
- low[v] = min(low[v], low[u]) +//          d = distance vector (defined only if has_cycle == false) 
- daca ​(low[u> idx[v]) +//          p = parent vector (defined only if has_cycle == false) 
- (vueste muchie critica +// 
- altfel +Bellman-Ford(source,​ G=(nodes, edges)) { 
- low[v] = min(low[v]idx[u])+  // STEP 0: initialize results 
 +  // d[node] = distance from source to node 
 +  // p[node] = parent of node on the shortest path from source to node 
 +  ​foreach (node in nodes) { 
 +      d[node] ​= +oo;                          // distance not yet computed 
 +      p(node= null;                         // parent not yet found 
 +  } 
 + 
 +  // STEP 1: set distance and parent for source node 
 +  d[source= 0;                              // distance from source to source 
 +  ​p[source] = null;                           // source never has parent 
 + 
 +  // STEP 2: do |nodes| - 1 relaxations for all edges in G 
 +  for (i = 1 : |nodes| ​ - 1)  { 
 +    foreach ((nodeneigh) in edges) { 
 +      if (d[node] + w[node][neigh] < d[neigh]) {   // try to relax edge (nodeneigh
 +        d[neigh] = d[node+ w[node][neigh]; ​      // update the new distance from source to neigh 
 +        p[neigh] = node;                           // save parent 
 +      } 
 +    } 
 +  } 
 + 
 +  // STEP 3: check if edge relaxations can still be made 
 +  foreach ((node, neighin edges) { 
 +    ​if ​(d[node] + w[node][neigh< d[neigh]) {   // try to relax edge (node, neigh
 +      // negative cycle detected! 
 +      return true, null, null; 
 +    } 
 +  } 
 + 
 +  // STEP 4: no cycle detected 
 +  return false, d, p; 
 +
 + 
 +// Usage example: 
 +has_cycle, d, p = Bellman-Ford(sourceG=(nodes, edges)); 
 +if (has_cycle) { 
 +  print "Has Cycle!"​ 
 +  STOP. 
 +} else { 
 +  // 1. Use distances from d 
 +  // (e.g. d[node] = distance from source to node) 
 +  // 
 +  // 2. Rebuild path from node to source using parents ​(p) 
 +  RebuildPath(sourcedestination,​ p)
 +}
 </​code>​ </​code>​
 +==== Exemple ====
  
-Complexitate:​ O(|V| + |E|)+=== Exemplu Bellman-Ford ===
  
-{{ pa:laboratoare:​7.7.png?400 |}} +{{https://ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-bellman-example01.png?512Exemplu Bellman-Ford}}
-  +
-===== Componente biconexe =====+
  
-**O componenta biconexă (sau biconectată) este o componentă a grafului care nu conţine puncte ​de articulatie.** Astfel după eliminarea oricărui vârf din componenta curentă, restul vârfurilor vor rămâne conectate întrucât între oricare două vărfuri din aceeași componentă biconexă există cel puțin două căi disjuncte.+Drumurile minime calculate ​de algoritmul lui Bellman-Ford sunt:
  
-Astfel, pentru a determina componentele biconexe ale unui graf, vom adapta algoritmul de aflare a punctelor critice, reţinând şi o stivă cu toate muchiile de avansare şi de întoarcere parcurse până la un moment dat. La întâlnirea unui nod critic v se formează o nouă componentă biconexă pe care o vom determina extrăgând din stivă muchiile corespunzătoare. Nodul **v** este critic dacă am găsit un copil **u** din care nu se poate ajunge pe un nivel mai mic în arborele de adâncime pe un alt drum care foloseşte muchii de întoarcere **(low[u>= idx[v])**. Atunci când găsim un astfel de nod **u**, toate muchiile aflate în stivă până la muchia (v, u) inclusiv formează o nouă componentă biconexă.+|node|1|2|3|4|5| \\ 
 +|d[node]|0|-2|-2|-3|-2| \\ 
 +|p[node]|null|4|1|3|4| \\
  
-Nodul rădăcină trebuie tratat separat. Conform regulilor acestuia pentru a fi marcat nod critic (numărul de copii >= 2), este suficient să considerăm că fiecare copil face parte dintr-o componentă biconexă separată. 
  
-**Atenție! Împărțirea în componente biconexe a unui graf neorientat reprezintă o partiție disjunctă a muchiilor grafului (împreună cu vârfurile adiacente muchiilor corespunzătoare fiecărei componente în parte). Acest lucru implică că unele vârfuri pot face parte din mai multe componente biconexe diferite. Care sunt acestea?**+<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:
  
-ComplexitateO(|V| |E|) +  * ''​%%n = 5%%'',​ ''​%%m = 6%%''​ 
-   +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
-{{ pa:laboratoare:7.8.png?400 |}} +  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
-===== Concluzii ​=====+  * Alegem $source = 1$ și rulăm pas cu pas algoritmul. 
 +  * STEP 0: 
 +    * $d[node] = +∞$, pentru $node = 1:5n$ 
 +    * $p[node] = null$, pentru $node = 1:5$ 
 +  ​* STEP 1: initializări pentru sursă 
 +    * $d[1] = 0$ 
 +    * $p[1] = null$ 
 +  * STEP 2Avem 5 noduri, relaxăm de 4 ori ($n - 1$ ori) toate muchiile din graf. Ordinea nu contează, pentru simplitate vom relaxa muchiile în ordine lexico-grafică$(1, 2); (1, 3); (2, 3); (3, 4); (4, 2); (4, 5)$. 
 +    * **iterație relaxare #1**: 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < +∞$)**DA**, atunci **relaxarea muchiei are loc**: 
 +        * $d[2] d[1] + w[1][2] ​0 - 1 -1$ (actualizăm distanța) 
 +        * $p[2] 1$ (actualizăm părintele de pe drum) 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < +∞$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[3] 0 - 2 -2$ 
 +        * $p[3] 1$ 
 +      * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-1 + 0 < -2$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-2 - 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[4] -2 - 1 -3$ 
 +        * $p[4] 3$ 
 +      * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-3 + 1 < -1$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[2] = -3 + 1 = -2$ 
 +        * $p[2] = 3$ 
 +      * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-3 + 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[5] = -3 + 1 = -2$ 
 +        * $p[5] = 4$ 
 +    * **iterație relaxare #2**: Nu se va relaxa nicio o muchie. 
 +    * **iterație relaxare #3**: Nu se va relaxa nicio o muchie. 
 +  * STEP 3: Se incearcă o etapă în plus (#4) de relaxări. Pentru că nu se mai poate relaxa muchii, înseamnă că distanțele găsite la ''​%%STEP 2%%''​ sunt finale și graful nu conține ciclu de cost negativ! 
 +  * STOP.
  
-Algoritmul de parcurgere în adâncime poate fi modificat pentru calculul componentelor tare conexe, a punctelor de articulaţie,​ a punţilor şi a componentelor biconexe. Complexitatea acestor algoritmi va fi cea a parcurgerii:​ O(|V| + |E|).+</​spoiler>​ \\
  
-===== Referinte ===== 
  
-[1Introducere in Algoritmi, Thomas H. Cormen, Charles E. Leiserson, Ronald L. – Capitolul 23 Algoritmi elementari pe grafuri [[http://​net.pku.edu.cn/​~course/​cs101/​resource/​Intro2Algorithm/​book6/​chap23.htm]]+=== [Studiu de cazExemplu Bellman-Forddetecție ciclu de cost negativ ===
  
-[2] Wikipedia – Algoritmul lui Tarjan [[http://en.wikipedia.org/​wiki/​Tarjan%27s_strongly_connected_components_algorithm]]+Dacă graful are un ciclu de cost negativ, se va putea plimba pe acel ciclu la infinit, reactulizând distanțele nodurilor implicate.
  
-[3] Wikipedia – Algoritmul lui Kosaraju [[http://en.wikipedia.org/wiki/Kosaraju%27s_algorithm]]+<spoiler Exemplu detecție ciclu de cost negativ cu Bellman-Ford>​ {{https://ocw.cs.pub.ro/courses/_media/​pa/​new_pa/​lab09-graph-bellman-example02.png?​512| Exemplu Bellman-Ford - ciclu negativ}}
  
-[4] Wikipedia - Componente biconexe [[http://​en.wikipedia.org/​wiki/​Biconnected_component]]+În exemplul atașat, avem un graf **orientat** cu următoare configurație:
  
-[5] Infoarena ​Arhiva educationala ​Componente tari conexe ​[[http://infoarena.ro/​problema/​ctc]]+  * ''​%%n = 5%%'',​ ''​%%m = 6%%''​ 
 +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
 +  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
 +  * Alegem $source = 1$ și rulăm pas cu pas algoritmul. 
 +  * STEP 0: 
 +    * $d[node] = +∞$, pentru $node = 1:5n$ 
 +    * $p[node] = null$, pentru $node = 1:5
 +  * STEP 1: initializări pentru sursă 
 +    * $d[1= 0$ 
 +    * $p[1] = null$ 
 +  * STEP 2: Avem 5 noduri, relaxăm de 4 ori ($n 1$ ori) toate muchiile din graf. Ordinea nu contează, pentru simplitate vom relaxa muchiile în ordine lexico-grafică: $(1, 2); (1, 3); (2, 3); (3, 4); (4, 2); (4, 5)$. 
 +    * **iterație relaxare #1**: 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < +∞$). **DA**, atunci **relaxarea muchiei are loc**: 
 +        * $d[2] = d[1] + w[1][2] = 0 - 1 = -1$ (actualizăm distanța) 
 +        * $p[2] = 1$ (actualizăm părintele de pe drum) 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < +∞$)**DA**, atunci relaxarea muchiei are loc: 
 +        * $d[3= 0 - 2 = -2$ 
 +        * $p[3= 1$ 
 +      * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-1 - 2 < -2$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[3] = -1 -2 = -3$ 
 +        * $p[3] = 2$ 
 +      * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-3 - 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[4] = -3 - 1 = -4$ 
 +        * $p[4] = 3$ 
 +      * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-4 + 1 < -1$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[2] = -3 + 1 = -2$ 
 +        * $p[2] = 4$ 
 +      * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-3 + 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[5] = -3 + 1 = -2$ 
 +        * $p[5] = 4$ 
 +    * **iterație relaxare #2**: 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -2$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -3$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-2 - 2 < -2$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[3] = -2 -2 = -4$ 
 +        * $p[3] = 2$ 
 +      * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-4 - 1 < -4$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[4] = -4 - 1 = -5$ 
 +        * $p[4] = 3$ 
 +      * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-5 + 1 < -2$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[2] = -5 + 1 = -4$ 
 +        * $p[2] = 4$ 
 +      * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-5 + 1 < -2$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[5] = -5 + 1 = -4$ 
 +        * $p[5] = 4$ 
 +    * **iterație relaxare #3**: 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -4$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -4$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-4 - 2 < -4$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[3] = -4 -2 = -6$ 
 +        * $p[3] = 2$ 
 +      * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-6 - 1 < -5$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[4] = -6 - 1 = -7$ 
 +        * $p[4] = 3$ 
 +      * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-7 + 1 < -4$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[2] = -7 + 1 = -6$ 
 +        * $p[2] = 4$ 
 +      * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-7 + 1 < -4$). **DA**, atunci relaxarea muchiei are loc: 
 +        * $d[5] = -7 + 1 = -6$ 
 +        * $p[5] = 4$ 
 +  * STEP 3: Se incearcă o etapă în plus (#4) de relaxări. 
 +    * **iterație relaxare #4**: 
 +      * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -6$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -6$). **NU**, atunci relaxarea muchiei **NU** are loc. 
 +      * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-6 - 2 < -6$). **DA**, atunci relaxarea muchiei **se poate face**. 
 +        * În acest moment putem trage **concluzia că avem un ciclu de cost negativ în graf**, căci altfel distanțele ar fi fost finale și nu am mai fi putut relaxa ceva în ''​%%STEP 3%%''​. 
 +  * STOP.
  
-[6] Infoarena - Arhiva educationala - Componente biconexe ​   [[http://​infoarena.ro/​problema/​biconex]]+</spoiler> \\
  
-[7Infoarena ​Arhiva educationala ​2SAT [[http://infoarena.ro/problema/2sat]]+ 
 +==== Complexitate ==== 
 + 
 +  * **complexitate temporală**:​ $T = O(n * m)\ sau\ O(|V| * |E|)$ 
 +  * **complexitate spațială** : $S = O(1)$ 
 + 
 +<spoiler Detalii (analiză + optimizări)>​ 
 + 
 +  * **complexitate temporală**:​ Algoritmul relaxează de **n** ori toate cele **m** muchii din graf. 
 +  * **complexitate spațială** : Nu avem memorie spațială auxialiară. ATENȚIE! Vom aloca tablourile $d$ / $p$, însă acestea nu sunt specifice algoritmului. 
 +  * **optimizare**:​ Se poate obține o performanță mai bună în practică, dar cu aceeași complexitate asimptotică,​ folosind o coadă: 
 +    * Dacă $d[node]$ nu a fost schimbată la un pas, folosirea muchiilor care pornesc din $node$ nu produce efecte. 
 +    * Se poate ţine o coadă de noduri, la fiecare pas scoţând un element din aceasta. Se va încerca relaxarea muchiilor care pornesc din nodul scos. Nodurile cu distanțe actualizate se reintroduc în coadă. 
 +    * Dacă un nod a fost scos de **n** ori din coadă, atunci graful conține cel puțin un ciclu de cost negativ. 
 + 
 +</​spoiler>​ \\ 
 + 
 + 
 +===== TLDR ===== 
 + 
 +  * Pentru **topologii particulare** ale problemei **shortest-paths**, vom folosi mereu un algoritm baza pe o parcugere (de exemplu, sortare topologic la DAG). Folosirea algoritmilor Dijkstra / Bellman-Ford ar duce la o soluție ineficientă pentru aceste cazuri particulare! 
 +  * Pe **topologii generale**, pentru **single-source shortest-paths problem**, vom folosi Dijkstra ​Bellman-Ford. 
 +    * **Dijkstra** are complexitate mai bună, deci este de preferat, însă acesta se poate folosi doar pe grafuri cu costuri **nenegative**. 
 +    * Dacă avem muchii cu costuri negative, suntem obligați să folosim **Bellman-Ford**,​ care ne poate detecta și prezența ciclurilor de cost negativ. 
 +  * Dacă avem mai multe surse destinații,​ putem rula pentru fiecare sursă Dijkstra ​/Bellman-Ford,​ obținând complexitate $O(n * m * log n)$ / $O(n^2 * m)$. Vom vedea în laboratorul următor cum putem îmbunătăți acest lucru. 
 + 
 +===== Exerciții =====
  
  
-===== Exercitii ===== 
 <​note>​ <​note>​
-In acest laborator vom folosi scheletul de laborator din arhiva {{pa:​new_pa:​skel-lab08.zip}}. 
-</​note>​ 
  
 +Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab09|pa-lab::​skel/​lab09]].
 +
 +</​note>​
 <note warning> <note warning>
-Inainte de a rezolva exercitiile,​ asigurati-va ca ati citit si inteles toate precizarile din sectiunea 
-[[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]]. 
  
-Prin citirea acestor ​precizari va asigurati ca+Înainte de a rezolva exercițiile,​ asigurați-vă că ați citit și înțeles toate precizările din secțiunea [[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]]. 
-   ​cunoasteti ​**conventiile** folosite + 
-   ​evitati ​**buguri** +Prin citirea acestor ​precizări vă asigurați că
-   ​evitati ​**depunctari** la lab/​teme/​test+ 
 +  ​știți ​**convențiile** folosite 
 +  evitați ​**buguri** 
 +  evitați ​**depunctări** la lab/​teme/​test 
  
 </​note>​ </​note>​
  
-=== CTC === +==== Dijkstra ​==== 
-Se da un graf **orientat** cu **n** noduri ​si **m** arce. Sa se gaseasca ​**componentele tare-conexe**.+ 
 +Se dă un graf **orientat** cu **n** noduri ​și **m** arce. Graful are pe arce **costuri pozitive** (nenegative). 
 + 
 +Folosiți **Dijkstra** pentru a găsi **costul minim** (**lungimea minimă**) a unui drum de la o sursă dată (**source**) la toate celelalte $n - 1$ noduri din graf. 
  
 <note warning> <note warning>
-Restrictii si precizari: 
-  * $ n <= 10^5 $ 
-  * $ m <= 2 * 10^5 $ 
-  * timp de executie 
-    * C++: 1s 
-    * Java: 2s 
-</​note>​ 
  
 +Restricții și precizări:
 +
 +  * $ n <= 50.000 $
 +  * $ m <= 2.5 * 10^5 $
 +  * $ 0 <= c <= 20.000$, unde c este costul/​lungimea unui arc
 +  * timp de execuție
 +    * C++: ''​%%1s%%''​
 +    * Java: ''​%%2s%%''​
 +
 +
 +</​note>​
 <​note>​ <​note>​
-Rezultatul se va returna sub forma unui vector, ​unde fiecare element ​este un vector ​(o CTC).+ 
 +Rezultatul se va returna sub forma unui vector ​**d** cu **n + 1** elemente. 
 + 
 +Convenție:​ 
 + 
 +  * $d[node]$ = costul minim / lungimea minimă a unui drum de la **source** la nodul **node** 
 +  * $d[source] = 0$ 
 +  * $d[node] = -1$dacă nu se poate ajunge de la **source** la **node** 
 + 
 +$d[0]$ nu este folosit, deci ca fi initializat cu 0! (am pastrat indexarea nodurilor de la 1). 
 </​note>​ </​note>​
  
 +==== RebuildPath ====
  
 +S-a rulat anterior un algoritm oarecare de calculare a drumurilor de lungime minimă într-un graf cu **n** noduri și **m** arce folosind sursa **source**. Se cunoaște vectorul de părinți $p$ rezultat:
 +
 +  * $p[node]$ = părintele lui node de pe drumul minim de la sursă la node.
 +  * $p[source] = 0$ - sursa nu are părinte.
 +  * $p[node] = 0$ - nodul $node$ nu este accesibil din $source$.
 +
 +Se cere să se reconstituie drumul de la nodul **source** la nodul **destination**.
  
-=== CUT VERTEX === 
-Se da un graf **neorientat conex** ​ cu **n** noduri si **m** muchii. Se cere sa se gaseaca toate **punctele critice**. 
  
 <note warning> <note warning>
-Restrictii si precizari: 
-  * $ n <= 10^5 $ 
-  * $ m <= 2 * 10^5 $ 
-  * timp de executie 
-    * C++: 1s 
-    * Java: 2s 
-</​note>​ 
  
 +Restricții și precizări:
 +
 +  * $ n <= 50.000 $
 +  * timp de execuție
 +    * C++: ''​%%1s%%''​
 +    * Java: ''​%%2s%%''​
 +
 +
 +</​note>​
 <​note>​ <​note>​
-Rezultatul se va returna sub forma unui vector ​cu ** ** elementeunde X este numarul de puncte critice din graf.+ 
 +Rezultatul se va returna sub forma unui vector **path**: $path = (source, ..., destination)$. 
 + 
 </​note>​ </​note>​
  
-=== CRITICAL EDGE === +==== Bellman-Ford ​==== 
-Se da un graf **neorientat conex**  cu **n** noduri ​si **m** muchiiSe cere sa se gaseaca toate **muchiile critice**.+ 
 +Se dă un graf **orientat** cu **n** noduri ​și **m** arceGraful are pe arce **costuri oarecare** (pozitive sau negative). 
 + 
 +Folosiți **Bellman-Ford** pentru a găsi **costul minim** (**lungimea minimă**) a unui drum de la o sursă dată (**source**) la toate celelalte $n - 1$ noduri din graf. Se va folosi aceeași convenție de reprezentare a vectorului de distanțe $d$. 
  
 <note warning> <note warning>
-Restrictii si precizari: 
-  * $ n <= 10^5 $ 
-  * $ m <= 2 * 10^5 $ 
-  * timp de executie 
-    * C++: 1s 
-    * Java: 2s 
-</​note>​ 
  
-<​note>​ +Restricții și precizări:
-Rezultatul se va returna sub forma unui vector cu ** X ** elemente, unde X este numarul de muchii critice. +
-</​note>​+
  
-=== BONUS === +  * $ n <50.000 $ 
-Se da un graf **neorientat conex** ​ cu **n** noduri si **m** muchii. Se cere sa se gaseaca toate **componentele biconexe**.+  * <= 2.5 10^5 $ 
 +  ​$ -1000 <= c <= +1000$, unde c este costul/​lungimea unui arc 
 +  ​timp de execuție 
 +    ​C++: ''​%%1s%%''​ 
 +    ​Java: ''​%%2s%%''​ 
 + 
 + 
 +</​note>​
  
 +==== BONUS ====
  
-Pentru testare **trebuie ** sa folositi problema [[ +La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra.
-https://​infoarena.ro/​problema/​biconex| biconex]] de pe infoarena.+
  
 +==== Extra ====
  
-=== Extra === +  * [[https://​infoarena.ro/​problema/​distante|infoarena/​distante]] 
-<spoiler retele>​ +  * [[https://​infoarena.ro/​problema/​catun|infoarena/​catun]] 
-Rezolvati ​problema [[https://​infoarena.ro/​problema/​reteleretele]] pe infoarena. +  * [[https://​infoarena.ro/​problema/​lanterna|infoarena/​lanterna]] 
-</spoiler>+  * [[https://infoarena.ro/​problema/​ciclu|infoarena/​ciclu]] 
 +  * [[https://​codeforces.com/​contest/​464/​problem/​E|codeforces/​the-classic-probem]] 
 +  * [[https://​codeforces.com/​contest/​229/​problem/​B|codeforces/​planets]] 
 +  * [[http://​poj.org/​problem?​id=1734|acm/​sightseeingtrip]]
  
-<spoiler clepsidra>​ +===== Referințe =====
-Rezolvati ** ACASA** problema [[https://​infoarena.ro/​problema/​clepsidra| clepsidra]] pe infoarena. +
-</​spoiler>​+
  
 +[0] Chapters **Single-Source Shortest Paths** / **All-Pairs Shortest Paths**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.
  
-<spoiler Course schedule>​ 
-Rezolvati ** ACASA** problema [[https://​leetcode.com/​problems/​course-schedule/​description/​| course-schedule]] pe leetcode. 
-(aplicatie tipuri de muchii) 
-</​spoiler>​ 
pa/laboratoare/laborator-08.1555366782.txt.gz · Last modified: 2019/04/16 01:19 by mihaela.balint
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