Differences

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

Link to this comparison view

pa:laboratoare:laborator-09 [2017/05/02 02:11]
andrei_mario.dinu [1.Cablare optimă în rețele de date (10p)]
pa:laboratoare:laborator-09 [2024/05/09 18:24] (current)
radu.nichita
Line 1: Line 1:
-====== Laborator 9 - Arbori Minimi de Acoperire ​======+====== Laborator 09: Drumuri minime în grafuri: surse / destinații multiple. (2/2) ====== 
 + 
 + 
 +{{:​pa:​new_pa:​partners:​adobe-logo.png?​155 |}} 
 +\\ \\ \\ 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 images, videos, and apps, and transform how companies interact with customers across every screen.
  
 ===== Obiective laborator ===== ===== Obiective laborator =====
-  *Însuşirea conceptului de arbore minim de acoperire; 
-  *Înţelegerea modului de funcţionare a algoritmilor de determinare a unui arbore minim de acoperire prezentaţi;​ 
-  *Aplicarea algoritmilor în rezolvarea problemelor;​ 
  
-===== Importanţă – aplicaţii practice =====+În laboratorul anterior am introdus contextul pentru **Shortest-paths problem** și **Single-source shortest-paths problem**. În laboratorul 10 vom continua cu **All-pairs shortest-paths problem**.
  
-Găsirea unui arbore minim de acoperire pentru un graf are aplicaţii în domenii cât se poate de variate: +  ​Înțelegerea conceptelor ​de cost asociat unei muchiirelaxare ​a unei muchii
-  ​*Reţele (de calculatoare,​ telefonie, cablu TV, electricitate,​ drumuri): se doreşte interconectarea mai multor puncte, cu un cost redus şi atunci este utilă cunoaşterea arborelui care conectează toate punctelecu cel mai mic cost posibil. STP(Spanning Tree Protocol) este un protocol de rutare care previne apariţia buclelor într-un LAN, şi se bazează pe crearea unui arbore de acoperire. Singurele legături active sunt cele care apar în acest arbore, iar astfel se evită buclele. +  * Prezentarea problemei drumului ​de cost minim (diverse variante)
-  *Segmentarea imaginilor: împărţirea unei imagini în regiuni de pixeli cu proprietăţi asemănătoare. E utilă mai apoi în analiza medicală ​a unei zone afectate de o tumoare de exemplu+  * Prezentarea algoritmilor ​pentru ​calculul drumurilor minime.
-  *Algoritmi ​de aproximare pt probleme NP-dure: problema comis-voiajorului,​ arbori Steiner+
-  *Clustering: ​pentru ​detectarea de clustere cu forme neregulate [8], [9].+
  
-===== Descrierea problemei şi a rezolvărilor ​=====+===== Shortest-paths problem: all-pairs ​=====
  
-Dându-se un graf conex neorientat G =(V, E), se numeşte arbore de acoperire al lui G un subgraf G’=(V, E’)  care conţine toate vârfurile grafului G şi o submulţime minimă de muchii E’⊆ E cu proprietatea că uneşte toate vârfurile şnu conţine cicluriCum G’ este conex şi aciclic, el este arborePentru un graf oarecareexistă mai mulţarbori de acoperire.+Vă rugăm să parcugeț[[https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​shortest-paths-problem|Shortest-paths problem]] pentru a vă familiariza cu contextulproblema șnotațiile folosite.
  
-Dacă asociem o matrice de costuri, w, pentru muchiile din G, fiecare arbore de acoperire va avea asociat un cost egal cu suma costurilor muchiilor conţinute. Un arbore care are costul asociat mai mic sau egal cu costul oricărui alt arbore de acoperire se numeşte arbore minim de acoperire (minimum spanning tree) al grafului G. Un graf poate avea mai mulţi arbori minimi de acoperire. Dacă toate costurile muchiilor sunt diferite, există un singur AMA.  +Concepte necesare:
-Primul algoritm pentru determinarea unui arbore minim de acoperire a fost scris în 1926 de Otakar Boruvka. În prezent, cei mai folosiţi algoritmi sunt Prim şi Kruskal. Toţi trei sunt algoritmi greedy, şi rulează în timp polinomial. La fiecare pas, pentru a construi arborele se alege cea mai bună variantă posibilă la momentul respectiv. Generic, algoritmul de determinare a unui AMA se poate scrie astfel:+
  
-<code cpp> +  * **cost muchie** / **edge cost** 
-ArboreMinimDeAcoperire(G(V,​ E), c) +  * **cost drum** / **path cost** 
- MuchiiAMA ​∅; +  * **problema drumurilor minime: surse / destinații multiple** / **all-pairs shortest-paths problem** 
- while (MuchiiAMA nu reprezintă muchiile unui arbore minim de acoperire+  * **relaxarea unei muchii** / **edge relaxation** 
- găseşte o muchie ​(u, v) care este sigură pentru MuchiiAMA; +  * **reconstruirea unui drum** / **RebuildPath** 
- MuchiiAMA = MuchiiAMA ∪ {(uv)}; + 
- return MuchiiAMA; +===== Algoritmi ===== 
-</​code>​+ 
 +În acest laborator vom studia **all-pairs shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:​ 
 + 
 +  * **Roy-Floyd**:​ eficient pentru grafuri **dense** ​($m >> n$ sau $|E| >> |V|$ - a.k.a. număr de muchii mult mai decât număr ​de noduri). 
 +  * **Johnson**:​ eficient pentru grafuri **rare** ​($n >> m$ sau $|V| >> |E|$ - a.k.a. număr de noduri mult mai mare decât număr de muchii). 
 + 
 +Vom prezenta fiecare algoritmîl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare. 
 + 
 +Puteți consulta capitolul **All-Pairs Shortest Paths** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești algoritmi.
  
-O muchie sigură este o muchie care se poate adăuga unei submulţimi de muchii ale unui arbore minim de acoperire, astfel încât noua mulţime obţinută să aparţină tot unui arbore minim de acoperire. Iniţial, MuchiiAMA este o mulţime vidă. La fiecare pas, se adaugă câte o muchie sigură, deci MuchiiAMA rămâne o submulţime a unui AMA. În consecinţă,​ la sfarşitul rulării algoritmului (când muchiile din mulţime unesc toate nodurile din graf), MuchiiAMA va conţine de fapt arborele minim de acoperire dorit. +===== Roy-Floyd =====
-  +
-==== Algoritmul Kruskal ​====+
  
-Algoritmul ​a fost dezvoltat în 1956 de Joseph KruskalDeterminarea arborelui minim de acoperire se face prin reuniuni de subarbori minimi de acoperireIniţial, se consideră că fiecare nod din graf este un arbore. Apoi, la fiecare pas se selectează muchia de cost minim care uneşte doi subarbori disjuncţi, şi se realizează unirea celor doi subarbori. Muchia respectivă se adaugă la mulţimea MuchiiAMA, care la sfârşit va conţine chiar muchiile din arborele minim de acoperire+Algoritmul ​[[https://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm|Roy-Floyd]] (**Roy-Floyd** / **Floyd–Warshall** algorithm) rezolvă **shortest-paths problem** în grafuri **G = (VE)** care sunt **dense**.
  
-====Pseudocod ​=====+==== Roy-Floyd - Pseudocod ====
  
 <code cpp> <code cpp>
-Kruskal(G(VE)w) +// apply Roy-Floyd'​s algorithm for all-pairs shortest-paths problem 
-MuchiiAMA <∅; +// 
-for each v in V do +// nodes     = list of all nodes from G 
- MakeSet(v); //fiecare nod e un arbore diferit +// adj[node] = the adjacency list of node 
-sort(E); //sortează muchiile în ordine crescătoare a costului +//             ​example:​ adj[node] = {..., neigh, ...} => edge (nodeneighof cost w[node][neigh] 
-for each (u,v) in E do +// 
- if (FindSet(u!= FindSet(v)then //capetele muchiei fac parte //din subarbori disjuncţ+// 
- MuchiiAMA = MuchiiAMA ∪ {(u, v)}; //adaugă muchia la arbore +// returns: d, p 
- Union(uv);    //uneşte subarborii corespunzători lui u şi v +//          d = distance matrix 
-return MuchiiAMA;+//          p = parent matrix 
 +// 
 +Roy-Floyd(G=(nodes,​ adj)) { 
 +  // STEP 1: initialize results 
 +  // d[i][j] = minimum distance from i to j 
 +  // p[i][j] = parent of node j, on shortest path from i to j 
 +  ​for (i in nodes) { 
 +    ​for ​(j in nodes
 +      d[i][j] = w[i][j]                         // edge cost (or infinity if missing) 
 +      ​p[i][j] = (w[i][j] != infinity ? i : null); // parent (or null if missing) 
 +    } 
 +  } 
 + 
 +  // STEP 2: For each intermediar node k, 
 +  // try to update shortest path from i to j. 
 +  for (k in nodes) { 
 +    ​for ​(i in nodes
 +      for (j in nodes
 +        ​// Is (-> ... -> k) reunion with (k -> ... -> j) shorter than (i -> ... -> j)? 
 +        ​if ​(d[i][k] + d[k][j] < d[i][j]
 +          d[i][j] = d[i][k] + d[k][j]; 
 +          p[i][j] = p[k][j]; 
 +        ​} 
 +      } 
 +    } 
 +  } 
 + 
 +  return d, p; 
 +
 + 
 +// Usage example: 
 +d, p = Roy-Floyd(G=(nodesadj)); 
 +// 1. Use distances from d 
 +// 2. Rebuild path from node to source using parents (p) 
 +RebuildPath(source,​ destination,​ p);
 </​code>​ </​code>​
 +==== Exemple ====
  
-Bucla principală for poate fi înlocuită cu o buclă while, în care se verifică dacă în MuchiiAMA există mai puţin de |V| 1 muchii, pentru că orice arbore de acoperire are |V| - 1 muchii, iar la fiecare pas se adaugă o muchie sigură. +=== Exemplu Roy-Floyd ===
- +
  
-===== Exemplu ​de rulare =====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-royfloyd-example.png?​512| ​Exemplu ​Roy-Floyd}}
  
-Se consideră graful ​ din figura următoare:+Drumurile minime calculate de algoritmul Roy-Floyd sunt:
  
-Fiecare subarbore va fi colorat diferit. Cum iniţial fiecare nod reprezintă un subarbore, nodurile au culori diferite. Pe măsură ce subarborii sunt uniţi, nodurile aparţinând aceluiaşi subarbore vor fi coloraţi identic. Costurile muchiilor sunt sortate în ordine crescătoare.+|-|1|2|3|4| \\ 
 +|1|0|-1|-2|0| \\ 
 +|2|4|0|2|4| \\ 
 +|3|5|1|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-{{ pa:​laboratoare:​92.jpg |}} 
  
-**Pas 1**+<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:​
  
-Se alege prima muchie, (1,4). Se observă că uneşte subarborii {1} şi {4}deci muchia e adăugată la MuchiiAMAiar cei doi subarbori se unesc+  * ''​%%n = 4%%''​''​%%m = 5%%''​ 
-{{ pa:​laboratoare:​93.jpg |}} +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
-MuchiiAMA ​{(1,4)}.+  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
 +  * Construim tabloul **d** al distanțelor minime: 
 +    * **d[u][v] = w[u][v]**, dacă există muchia $(uv)$. 
 +    * **d[u][v] = +∞dacă **NU%%**%% există muchia $(uv)$
 +    * **d[u][u] ​0**convenție. 
 +    * Obținem matricea **d_0**:
  
-**Pas 2**+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|+∞| \\ 
 +|2|4|0|3|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|+∞|-1|+∞|0| \\
  
-Următoarea muchie este (7,8), care uneşte {7} şi {8}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori. 
-{{ pa:​laboratoare:​94.jpg |}}  
-MuchiiAMA = {(1,​4),​(7,​8)}. 
  
-**Pas 3**+  * Rulăm algoritmul Roy-Floyd, vom scrie matricea pentru fiecare $ k = 1, 2, 3, 4$. 
 +    * $d_1$ (matricea dupa primul pas din algoritm; se modifică doar **d[2][3]**)
  
-Următoarea muchie este (5,6), care uneşte {5} şi {6}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori. +|-|1|2|3|4| \\ 
-{{ pa:​laboratoare:​95.jpg ​|}}  +|1|0|+∞|-2|+∞| \\ 
-MuchiiAMA = {(1,4),​(7,​8),​(5,​6)}.+|2|4|0|**2**|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|+∞|-1|+∞|0| \\
  
-**Pas 4** 
  
-Următorul cost este 4. Se observă că muchiile ​(1,2) şi (2,4) au costul 4 şi unesc {2} cu {1,4}. Se adaugă la MuchiiAMA una dintre cele două muchii, fie ea (1,2), şi se unesc cei doi subarbori. Alegerea muchiei (2,4) va duce la găsirea unui alt AMA. [Am spus anterior că un graf poate avea mai mulţarbori minimi de acoperire, cu acelaşi cost, dacă există muchii diferite cu acelaşi cost.] +  * $d_2$ (matricea dupa primul pas din algoritm; ​se modifică doar **d[4][3]** ș**d[4][1]**)
-{{ pa:​laboratoare:​96.jpg |}}  +
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2)}.+
  
-**Pas 5**+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|+∞| \\ 
 +|2|4|0|2|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|**3**|-1|**1**|0| v
  
-Următoarea muchie de cost minim este (5,8), care uneşte {5,6} şi {7,8}. Se adaugă la MuchiiAMA şi se unesc cei doi subarbori, rezultând {5,​6,​7,​8}. +  * $d_3$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][4]** și **d[2][4]**)
-{{ pa:​laboratoare:​97.jpg |}}  +
-MuchiiAMA = {(1,4),​(7,​8),​(5,​6),​(1,​2), (5,8)}.+
  
-**Pas 6**+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|**0**| \\ 
 +|2|4|0|2|**4**| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-Muchia (5,7), care are cel mai mic cost actual, are ambele extremităţi în subarborele {5,6,7,8}. În consecinţă,​ nu se efectuează nicio schimbare. 
  
-MuchiiAMA = {(1,4),​(7,​8),​(5,​6),​(1,2),(5,8)}.+  * $d_4$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][2]****d[3][1]** și **d[3][2]**)
  
-**Pas 7**+|-|1|2|3|4| \\ 
 +|1|0|**-1**|-2|0| \\ 
 +|2|4|0|2|4| \\ 
 +|3|**5**|**1**|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-Următorul cost este 7. Se observă că muchiile (1,6) şi (4,5) au costul 7 şi unesc subarborii {1,2,4} şi {5,6,7,8}. Se adaugă la MuchiiAMA (1,6), şi se unesc cei doi subarbori. Alegerea muchiei (4,5) va duce la găsirea unui alt AMA. 
-{{ pa:​laboratoare:​98.jpg |}}  
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6)}. 
  
-**Pas 8**+  ​Drumurile minime sunt finale (cele menționate anterior - $d_4$).
  
-Muchia (4,6) de cost 8 are capetele în acelaşi subarbore, deci nu se produc schimări.+</​spoiler>​ \\
  
-MuchiiAMA = {(1,​4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6)}. 
  
-**Pas 9**+==== Complexitate ====
  
-Muchia ​(1,3) de cost 9 uneşte cei doi subarbori rămaşi, {1,​2,​4,​5,​6,​7,​8} şi {3}. Deci după unire obţinem un singur arbore. ​(1,3) se adaugă la MuchiiAMA, care va conţine acum 7 muchii, iar algoritmul se opreşte.  +  * **complexitate temporală**:​ $T = O(n^3)\ sau\ O(|V|^3)
-{{ pa:laboratoare:​91.jpg |}}  +  * **complexitate spațială** $S = O(1)$
-Arborele minim de acoperire obţinut este {(1,4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(1,​6),​ (1,​3)}.Costul său se calculează însumând costurile tuturor muchiilor:+
  
-Cost(MuchiiAMA= 1 + 2 + 3 + 4 + 5 + 7 + 9 = 31 +<spoiler Detalii ​(analiză + optimizări)>
  
-Alţi arbori minimi de acoperire ​pentru ​exemplul propus sunt: +  * **complexitate temporală**:​ Se aplică recurența discutată anterior care pentru ​fiecare nod intermediar **k**, încearcă să actualizeze drumul minim de la **i** la **j**. Cele foruri dau complexitatea temporară. 
-{(1,4),​(7,​8),​(5,​6),​(1,​2),​(5,​8),​(4,​5),​ (1,3)} +  **complexitate spațială** : Nu stocăm tablouri auxilare.
-{(1,​4),​(7,​8),​(5,​6),​(2,​4),​(5,​8),​(1,​6),​ (1,3)} +
-{(1,​4),​(7,​8),​(5,​6),​(2,​4),​(5,​8),​(4,​5),​ (1,3)}.+
  
-Pentru alte exemple explicate consultaţi [2], [3] şi [5].+</​spoiler>​ \\
  
-===== Complexitate ===== 
  
-Timpul de execuţie depinde de implementarea structurilor de date pentru mulţimi disjuncte. Vom presupune că se foloseşte o pădure cu mulţimi disjuncte[cor]. Iniţializarea se face într-un timp O(|V|). Sortarea muchiilor în funcţie de cost se face în O(|E|log|E|). În bucla principală se execută |E| operaţii care presupun două operaţii de găsire a subarborilor din care fac parte extremităţile muchiilor şi eventual o reuniune a acestor arbori, într-un timp O(|E|log|E|). ​+===== Johnson =====
  
-Deci complexitatea totală esteO(|V|) + O(|E|log|E|) + O(|E|log|E|) = O(|E|log|E|).+Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Johnson%27s_algorithm|Johnson]] ​(**Johnson’s algorithm**rezolvă **shortest-paths problem** în grafuri **G = (V, E)** care sunt **rare**.
  
-Cum |E|<= |V|2şi log(|V|2) = 2log(|V|) = log(|V|), rezultă o complexitate O(|E|log|V|).+Ideea de la care pornește acest algoritm este de a rula cel mai rapid algorithm pentru **shortest-paths single source**adică algoritmul lui Dijkstra, pentru fiecare sursă ​(noddin graf. Dacă toate costurile sunt pozitiveputem face direct acest lucru. Dacă însă există costuri negative, nu putem aplica Dijkstra pe acest graf. Algoritmul lui Johnson face preprocesare ​(în **ComputeH**și calculează un graf echivalent, în care toate costurile sunt pozitive. Pe acest graf se poate aplica Dijkstra și să se afle toate distanțele. Ulterior se face translatarea inversă și se obțin **distanțele în graful inițial**.
  
-=== Algoritmul Prim ===+Observație:​ Dacă se știe că toate costurile din graf sunt pozitive (nenegative),​ algoritmul lui Johnson se poate înlocui cu rularea directă a algoritmului Dijkstra pentru toate nodurile din graf.
  
-Algoritmul a fost prima oară dezvoltat în 1930 de matematicianul ceh Vojtěch Jarnik, şi independent în 1957 de informaticianul Robert Prim, al cărui nume l-a luat. Algoritmul consideră iniţial că fiecare nod este un subarbore independent,​ ca şi Kruskal. Însă spre deosebire de acesta, nu se construiesc mai mulţi subarbori care se unesc şi în final ajung să formeze AMA, ci există un arbore principal, iar la fiecare pas se adaugă acestuia muchia cu cel mai mic cost care uneşte un nod din arbore cu un nod din afara sa. Nodul rădăcină al arborelui principal se alege arbitrar. Când s-au adăugat muchii care ajung în toate nodurile grafului, s-a obţinut AMA dorit. Abordarea seamănă cu algoritmul Dijkstra de găsire a drumului minim între două noduri ale unui graf.+==== Johnson ​Pseudocod ====
  
-Pentru o implementare eficientă, următoarea muchie de adăugat la arbore trebuie ​să fie uşor de selectatVârfurile care nu sunt în arbore trebuie sortate în funcţie de distanţa până la acesta (de fapt costul minim al unei muchii care leagă nodul dat de un nod din interiorul arborelui)Se poate folosi pentru aceasta o structură de heapPresupunând că (uveste muchia de cost minim care uneşte nodul u cu un nod v din arborese vor reţine două informaţii:+<code cpp> 
 +// apply Johnson'​algorithm for all-pairs shortest-paths problem 
 +// 
 +// nodes     = list of all nodes from G 
 +// adj[node] = the adjacency list of node 
 +//             ​example:​ adj[node] = {..., neigh, ...} => edge (nodeneighof cost w[node][neigh] 
 +// 
 +// returns: has_cycled, p 
 +//          has_cycle = negative cycle detection flag (true if found) 
 +//          d = distance matrix (defined only if has_cycle == false) 
 +//          p = parent matrix (defined only if has_cycle == false) 
 +// 
 +Johnson(G=(nodes,​ adj)) { 
 +  // STEP 1Compute adjustment distances h (using Bellmand-Ford). 
 +  has_cycle, h = ComputerH(G);​ 
 +  if (has_cycle) { 
 +    return true, null, null; 
 +  }
  
-  ​* d[u] = w[u,v] distanţa de la u la arbore +  ​// STEP 2: Update all costs in G to obtain all costs nonnegative. 
-  * p[u] predecesorul lui în drumul minim de la arbore la u.+  foreach ((u, v) in edges) { 
 +    if (w[u][v] !infinity) { 
 +      ​w[u][v] = w[u][v] + (h[u] - h[v]); 
 +    } 
 +  }
  
-La fiecare pas se va selecta nodul u cel mai apropiat de arborele principalreunind apoi arborele principal cu subarborele corespunzător nodului selectatSe verifică apoi dacă există noduri mai apropiate de decât de nodurile care erau anterior în arborecaz în care trebuie modificate distanţele dar şi predecesorul. Modificarea unei distanţe impune şi refacerea structurii de heap.+  // STEP 3: Now all costs are nonnegativeso we can apply Dijsktra. 
 +  // Start Dijkstra for each source ​u, saving just distances. 
 +  foreach (u in nodes) { 
 +    d_dijkstra, p_dijkstra = Dijkstra(u, G);
  
-===== Pseudocod =====+    // STEP 4: Compute distance (u, v) on initial graph. 
 +    foreach (v in nodes) { 
 +      d[u][v] ​d_dijkstra[v] + (h[v] - h[u]); 
 +      p[u][v] ​p_dijkstra[v];​ 
 +    } 
 +  }
  
-<code cpp> +  return false, d, p;  // no negative cycles detected 
-Prim(G(V,E), w, root) +}
-1. MuchiiAMA <- ∅; +
-2. for each u in V do +
-3. d[u] = INF;​ //​iniţial distanţele sunt infinit +
-4. p[u] = NIL; //şi nu există predecesori +
-5. d[root] = 0;​ //​distanţa de la rădăcină la arbore e 0 +
-6. H = Heap(V,​d);​ //​se construieşte heap-ul +
-7. while (H not empty) do //cât timp mai sunt noduri neadăugate +
-8. u = GetMin(H);​ //​se selectează cel mai apropiat nod u +
-9. MuchiiAMA = MuchiiAMA ∪ {(u, p[u])};//se adaugă muchia care uneşte u cu un nod din arborele principal ​ +
-10. for each v in Adj(u) do +
- //pentru toate nodurile adiacente lui u se verifică dacă +
- //trebuie făcute modificări +
-11. if w[u][v] < d[v] then +
-12. d[v] = w[u][v]; +
-13. p[v] = u; +
-14. Heapify(v,​ H);​ //​refacerea structurii de heap +
-15. MuchiiAMA = MuchiiAMA \ {(root, p[root])}+
-16. return MuchiiAMA;​ +
-</​code> ​+
  
-===== Exemplu de rulare =====+ComputeH(G=(nodes, adj)){ 
 +  // STEP 0: Create a new **temporary** graph 
 +  // * add a new node 
 +  source ​new node; 
 +  new_nodes ​nodes + { source }; 
 +  new_adj ​adj; 
 +  // * add a new edge (source, node) with cost 0 for all existing nodes 
 +  for (node in nodes) { 
 +    new_adj[source].push_back(node);​ 
 +    w[source][node] ​0; 
 +  }
  
-Se consideră graful folosit pentru exemplificarea algoritmului Kruskal.+  // STEP 1: Run Bellman-Ford on the new graphSave just flag and distances. 
 +  has_cycle, h, _ = Bellmann-Ford(source,​ new_G=(new_nodes,​ new_adj))) 
 +  if (has_cycle) { 
 +    return true, null; // negative cycle detected 
 +  }
  
-Iniţial fiecare nod reprezintă un arbore independentşi are o culoare unică. Se alege ca rădăcină a arborelui principal nodul 1. +  return false, d
 +}
  
-{{ pa:​laboratoare:​99.jpg |}}  
-**Pas 1** 
  
-Se alege muchia de cost minim care uneşte rădăcina cu un alt nod: (1,4de cost 1 si se adaugă arborelui principal. +// Usage example: 
-{{ pa:​laboratoare:​910.jpg |}}  +has_cycle, d, p = Johnson(sourceG=(nodes, adj)); 
-MuchiiAMA ​{(1,4)}.+if (has_cycle) ​{ 
 +  print "Has Cycle!"​ 
 +  STOP. 
 +else { 
 +  // 1. Use distances from d 
 +  // (e.g. d[node] ​distance from source to node) 
 +  // 
 +  // 3. Rebuild path from node to source using parents ​(p) 
 +  RebuildPath(sourcedestination,​ p)
 +} 
 +</​code>​ 
 +==== Exemple ====
  
-**Pas 2**+=== Exemplu Johnson ===
  
-Se alege muchia de cost minim care uneşte 1 sau 4 cu un alt nod. (1,2) şi (2,4) au ambele costul 4. Se alege una dintre ele, fie ea (1,2). +În această secțiune exemplificăm grafic cum rulează algoritmul lui Johnson pe un graf dat.
-{{ pa:​laboratoare:​911.jpg |}}  +
-MuchiiAMA = {(1,​4),​(1,​2)}.+
  
-**Pas 3**+<spoiler Explicație pas cu pas> {{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example01.png?​512| Exemplu Johnson 1/3}}
  
-Se alege următoarea muchie care uneşte {1,4,2} cu alt nod. (1,6) şi (4,5) au acelaşi cost, dar adaugă arborelui noduri diferite. La acest pas se selectează de exemplu (1,6). Selectând la acest pas (4,5) s-ar obţine un alt arbore de acoperire. +În exemplul atașatavem un graf **orientat** ​cu următoare configurație:
-{{ pa:laboratoare:​912.jpg |}}  +
-MuchiiAMA = {(1,​4),​(1,​2),​(1,​6)}.+
  
-**Pas 4**+  ​''​%%n = 5%%'',​ ''​%%m = 8%%''​ 
 +  ​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. (Observăm că nu avem ciclu de cost negativ, deci are sens să rulăm un algoritm de drumuri minime). 
 +  ​''​%%STEP 1%%'':​ Adăugăm un nod fictiv (exemplu nodul ''​%%6%%''​). Îl vom uni de fiecare nod din graful inițial (1, 2, 3, 4, 5) cu muchie de cost 0. Obținem graful din figura următoare:
  
-Se caută muchia de cost minim care uneşte ​{1,4,2,6} cu un alt nod, şi se găseşte (6,5) de cost 3Nodul 5 va fi adăugat arborelui principal. +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example02.png?512Exemplu Johnson - 2/3}}
-{{ pa:​laboratoare:​913.jpg |}}  +
-MuchiiAMA = {(1,​4),​(1,​2),​(1,​6),​(6,​5)}.+
  
-**Pas 5**+  ​Pe acest graf putem rula o dată algoritmul Bellman-Ford,​ considerând sursă noul nod adăugat. 
 +  ​Obținem vectorul de distanțe $h[node] = distanța\ de\ la\ nodul\ fictiv\ (6)\ la\ nodul\ node$
  
-Se alege muchia de cost minim care uneşte {1,4,2,6,5} cu un alt nod. (5,8) de cost 5 se adaugă listei de muchii. +|node|1|2|3|4|5|6| \\ 
-{{ pa:​laboratoare:​914.jpg ​|}}  +|h[node]|-1|-7|-4|0|-2|0| \\
-MuchiiAMA = {(1,4),(1,2),​(1,​6),​(5,​6),​(5,​8)}+
  
-**Pas 6** 
  
-Se alege muchia de cost minim (8,7care uneşte {1,4,2,6,5,8} cu nodul 7. +  * ''​%%STEP 2%%'':​ Revenim la graful inițial ​(cel cu 5 noduri8 muchiiși îi alterăm costurile:​ 
-{{ pa:​laboratoare:​915.jpg |}}  +    * $w[1][4] = w[1][4] + (h[1] - h[4]) = + [ (-1) - (0) ] = 1$ 
-MuchiiAMA ​{(1,4),(1,2),(1,6),(5,6),(5,8),(8,7)}.+    * $w[2][1] = w[2][1] + (h[2] - h[1]) = + [ (-7) - (-1) ] = 0$ 
 +    * $w[2][3] = w[2][3] + (h[2] - h[3]) = 3 + [ (-7) - (-4) ] = 0$ 
 +    * $w[3][1] ​w[3][1] + (h[3] - h[1]) = 4 + [ (-4) (-1) ] = 1$ 
 +    * $w[3][4] = w[3][4] + (h[3] - h[4]) = 5 + [ (-4) - (0) ] = 1
 +    * $w[4][2] = w[4][2] + (h[4] - h[2]= -7 + [ (0) - (-7) ] = 0$ 
 +    * $w[4][5] = w[4][2] + (h[4] - h[2]= -2 + [ (0) - (-2) ] = 0$ 
 +    * $w[5][4] = w[5][4] + (h[5] - h[4]= -1 + [ (-2- (-4) ] = 1$ 
 +  * Obținem graful din următoarea figură:
  
-**Pas 7** +{{https://ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example03.png?​512Exemplu Johnson - 2/3}}
-{{ pa:laboratoare:​916.jpg |}}  +
-Se alege muchia (1,3) de cost 9, care uneşte arborele principal cu ultimul nod ramas, şi se adaugă mulţimii de muchii. Cum toate nodurile sunt acoperite, am obţinut un arbore minim de acoperire.+
  
-MuchiiAMA ​= {(1,4),(1,2),(1,6),(5,6),(5,8),(8,7),(1,3)}.+  * ''​%%STEP 3%%'':​ Deoarece toate costurile sunt pozitive, putem rula Dijkstra pe rând, pentru fiecare sursă ''​%%source ​1, 2, 3, 4, 5%%''​ din graf. 
 +  * ''​%%STEP 3%%'':​ $ source = 1 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%1%%''​ pe graful inițial sunt: 
 +      * $d[1][1] = 0$ 
 +      * $d[1][2] = d_{dijkstra}[2] + (h[2] - h[1]) = 1 + [ (-7) - (-1) ] = -5$ 
 +      * $d[1][3] = d_{dijkstra}[3] + (h[3] - h[1]) = 1 + [ (-4) (-1) ] = -2$ 
 +      * $d[1][4] = d_{dijkstra}[4] + (h[4] - h[1]= 1 + [ (0(-1) ] = 2$ 
 +      * $d[1][5] = d_{dijkstra}[5] + (h[5] - h[1]= 1 + [ (-2(-1) ] = 0$
  
-Cost(MuchiiAMA) = + 4 + 7 + + 2 + 9 = 31 +|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|0|1|1|1|1| \\
  
-Alţi arbori minimi de acoperire pentru exemplul propus se pot obţine alegând diferit muchiile cu acelaşi cost (vezi paşii 2 şi 3) . 
  
-Pentru alte exemple explicate consultaţi [2], [4] şi [6].+|node|1|2|3|4|5| \\ 
 +|d[1][node]|0|-5|-2|2|0| \\
  
-===== Complexitate ===== 
  
-Iniţializările se fac în O(|V|)Bucla principală while se execută de |V| ori. Procedura GetMin() are nevoie de un timp de ordinul O(lg|V|), deci toate apelurile vor dura O(|V|lg|V|). Bucla for este executată în total de O(|E|) ori, deoarece suma tuturor listelor de adiacenţă este 2|E|. Modificarea distanţei, a predecesorului,​ şi refacerea heapului se execută într-un timp de O(1), O(1) şi respectiv O(lg|V|). Deci în total bucla interioară for durează O(|E|lg|V|).+  * ''​%%STEP 3%%'':​ $ source = 2 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%2%%''​ pe graful inițial sunt: 
 +      * $d[2][1] = d_{dijkstra}[1] + (h[1] - h[2]= 0 + [ (-1(-7] = 6$ 
 +      * $d[2][2] = 0$ 
 +      * $d[2][3] = d_{dijkstra}[3] + (h[3] - h[2]) = 0 + [ (-4) - (-7] = 3$ 
 +      * $d[2][4] = d_{dijkstra}[4] + (h[4] - h[2]) = + [ (0(-7] = 8$ 
 +      * $d[2][5] = d_{dijkstra}[5] + (h[5] - h[2]= 1 + [ (-2) - (-7) ] = 6$
  
-În consecinţă,​ timpul total de rulare este O(|V|lg|V|+|E|lg|V|), adică O(|E|lg|V|). Aceeaşi complexitate s-a obţinut şi pentru algoritmul Kruskal. Totuşi, timpul de execuţie al algoritmului Prin se poate îmbunătăţi până la O(|E|+|V|lg|V|), folosind heap-uri Fibonacci.+|node|1|2|3|4|5\\ 
 +|d_dijkstra[node]|0|0|0|1|1\\
  
-===== Concluzii ===== 
  
-Un arbore minim de acoperire al unui graf este un arbore care conţine toate nodurile, şi în plus acestea sunt conectate prin muchii care asigură un cost total minim. Determinarea unui arbore minim de acoperire pentru un graf este o problemă cu aplicaţii în foarte multe domenii: reţele, clustering, prelucrare de imagini. Cei mai cunoscuţi algoritmi, Prim şi Kruskal, rezolvă problema în timp polinomial. Performanţa algoritmilor depinde de modul de reprezentare a structurilor de date folosite. ​+|node|1|2|3|4|5| \\ 
 +|d[2][node]|6|0|3|8|6| \\
  
-===== Referinţe ===== 
  
-[1] – [[http://​en.wikipedia.org/​wiki/​Minimum_spanning_tree]]+  * ''​%%STEP 3%%'':​ $ source = 3 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%3%%''​ pe graful inițial sunt: 
 +      * $d[3][1] = d_{dijkstra}[1] + (h[1- h[3]) = 1 + [ (-1) - (-4) ] = 4$ 
 +      * $d[3][2] = d_{dijkstra}[2] + (h[2] - h[3]) = 1 + [ (-7) - (-4) ] = -2$ 
 +      * $d[3][3] = 0$ 
 +      * $d[3][4] = d_{dijkstra}[4] + (h[4] - h[3]) = 1 + [ (0) - (-4) ] = 5$ 
 +      * $d[3][5] = d_{dijkstra}[5] + (h[5] - h[3]) = 1 + [ (-2) - (-4) = 3$
  
-[2] – T. Cormen, C. Leiserson, R. Rivest, C. Stein – Introducere în Algoritmi, cap. 24+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|1|1|0|1|1| \\
  
-[3] – [[http://​en.wikipedia.org/​wiki/​Kruskal%27s_algorithm]] 
  
-[4– [[http://​en.wikipedia.org/​wiki/​Prim%27s_algorithm]]+|node|1|2|3|4|5| \\ 
 +|d[3][node]|4|-2|0|5|3| \\
  
-[5] – [[http://​w3.cs.upt.ro/​~calin/​resources/​sdaa/​kruskal.ppt]] 
  
-[6– [[http://​www.cs.upt.ro/​~calin/​resources/​sdaa/​prim.ppt]]+  * ''​%%STEP 3%%'':​ $ source = 4 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt: 
 +      * $d[4][1] = d_{dijkstra}[1+ (h[1] - h[4]) = 0 + [ (-1) - (0) ] = -1$ 
 +      * $d[4][2] = d_{dijkstra}[2] + (h[2] - h[4]) = 0 + [ (-7) - (0) ] = -7$ 
 +      * $d[4][3] = d_{dijkstra}[4] + (h[3] - h[4]) = 0 + [ (-4) - (0) ] = -4$ 
 +      * $d[4][4] = 0$ 
 +      * $d[4][5] = d_{dijkstra}[5] + (h[5] - h[4]) = 0 + [ (-2) - (0) = 2$
  
-[7] – [[http://​www.cs.princeton.edu/​~wayne/​kleinberg-tardos/​04mst.pdf]]+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|0|0|0|0|0| \\
  
-[8] – [[http://​hc.ims.u-tokyo.ac.jp/​JSBi/​journal/​GIW01/​GIW01F03.pdf]] 
  
-[9– [[http://​www4.ncsu.edu/​~zjorgen/​ictai06.pdf]]+|node|1|2|3|4|5| \\ 
 +|d[4][node]|-1|-7|-4|0|2| \\
  
-[10] – C. Giumale – Introducere în Analiza Algoritmilor,​ cap.5.5 
  
 +  * ''​%%STEP 3%%'':​ $ source = 5 $
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos.
 +    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt:
 +      * $d[5][1] = d_{dijkstra}[1] + (h[1] - h[5]) = 2 + [ (-1) - (-2) ] = 3$
 +      * $d[5][2] = d_{dijkstra}[2] + (h[2] - h[5]) = 2 + [ (-7) - (-2) ] = -3$
 +      * $d[5][3] = d_{dijkstra}[4] + (h[3] - h[5]) = 1 + [ (-4) - (-2) ] = -1$
 +      * $d[5][4] = d_{dijkstra}[5] + (h[4] - h[5]) = 2 + [ (0) - (-2) ] = 4$
 +      * $d[5][5] = 0$
  
-====== Probleme ======+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|2|2|1|2|0| \\
  
-==== 1. Cablare optimă în rețele de date ==== 
  
 +|node|1|2|3|4|5| \\
 +|d[5][node]|3|-3|-1|4|0| \\
 +
 +
 +  * STOP! Am obținut toate distanțele $d[u][v]$ cerute!
 +
 +</​spoiler>​ \\
 +
 +
 +==== Complexitate ====
 +
 +  * **complexitate temporală**:​ $T = O(n * m * log (n))\ sau\ O(|V| * |E| * log (|V|))$
 +  * **complexitate spațială** : $S = O(n + m)\ sau \ O(|V| + |E|)$
 +
 +<spoiler Detalii (analiză + optimizări)>​
 +
 +  * **complexitate temporală**:​
 +    * **ComputeH**:​
 +      * Construire graf nou - $O(n + m))$.
 +      * Aplicare Bellman-Ford pe noul graf - $O(n * m)$.
 +    * **Update edges** - pasul se face în $O(m)$.
 +    * Rularea Dijkstra pentru fiecare nod din graf - este complexitatea de la rulare Dijkstra pentru un singur nod sursă (a.k.a. $O(m log n)$), multiplicată cu numărul de noduri.
 +    * În final ajungem la un total de $O(n * m * log(n) + n * m + n + m) = O(n * m + log(n))$.
 +  * **complexitate spațială** : Se construiește un alt graf (**new_node** - n, **new_adj** - m), se produc câțiva vectori temporari de lungime n (**h**, **d_dijkstra**,​ **p_dijkstra**).
 +  * **optimizare**:​ Deoarece Dijsktra se poate optimiza (vezi laborator anterior), putem obține $O(n^2 * log n + n * m)$ complexitatea pentru ultima etapă. Complexitatea finală este $O(n ^ 2 log (m) + n * m)$.
 +
 +</​spoiler>​ \\
 +
 +
 +===== TLDR =====
 +
 +  * Pentru cazul **shortest-path single source** am studiat în laboratorul anterior algoritmul lui Dijkstra / algoritmul Bellman-Ford.
 +  * Pentru cazul **shortest-path all-pairs**,​ discuția se facă după numărul de muchii din graf:
 +    * **graf dens**: aplicăm Roy-Floyd și obținem $O(n ^ 3)$.
 +    * **graf rar**: aplicăm Johnson și obținem $O(n * m * log (n))$.
 +
 +===== Exerciții =====
 +
 +
 +<​note>​
 +
 +Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab10|pa-lab::​skel/​lab10]].
 +
 +</​note>​
 <note warning> <note warning>
-După rezolvarea problemeitoți studenții trebuie să încarce o arhivă cu sursele rezolvării pe Moodle (click ​[[http://cs.curs.pub.ro/2016/mod/assign/​view.php?​id=5715|aici]]) pentru a li se puncta laboratorul.+ 
 +Înainte de a rezolva exercițiileasiguraț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]]. 
 + 
 +Prin citirea acestor precizări vă asigurați că: 
 + 
 +  * știți **convențiile** folosite 
 +  * evitați **buguri** 
 +  * evitați **depunctări** la lab/​teme/​test 
 + 
 </​note>​ </​note>​
  
-Prietenul nostru Andrei a fost asignat drept noul șef al departamentul de rețelistică al companiei Nuke Cola. Sediul companiei are arondate N-1 sucursale, iar Andrei trebuie să asigure conectivitate între toate locațiile folosind o lungime minimă de fibră optică, lucru care duce implicit la reducerea costurilor totale. (AMA)+==== Roy-Floyd ====
  
-În cazul în care directorul general nu este de acord cu planul său, Andrei vrea să aibă un plan de backup, acesta reprezentând cea de-a doua cea mai bună soluție din punctul de vedere al lungimii totale de fibră optică utilizată(AMA2)+Se dă un graf **orientat** cu **n** noduri. Graful are **costuri strict pozitive**.
  
 +Se dă matricea ponderilor - **w**, se cere matricea drumurilor minime - **d**, aplicând algoritmul **Roy-Floyd**.
  
-Vă rugăm să îl ajutați pe Andrei să găsească configurația optimă având drept intrare distanțele între locațiile care pot fi conectate direct una cu cealaltă. 
  
-<hidden> +<note warning>
-Gigel este noul șef în departamentul de rețelistică a companiei Cola Maxim. Sediul companiei are un număr de încăperi. Gigel trebuie sa conecteze toate încăperile prin cabluri ethernet. El dorește să utilizeze cât mai puține cabluri, iar lungimea acestora să fie cât mai mică, pentru a reduce costurile. (AMA) **[5P]**+
  
-In caz că directorul general nu este de acord cu planul său, Gigel vrea să aibă un plan secundar, aceasta fiind cea de-a doua cea mai bună soluție dpdv al costului de implementare. (AMA2) **[5P]**+Restricții și precizări:
  
-Ajutați-l pe Gigel să determine cum să conecteze încaperile.  +  * $ n <= 100 $ 
-</​hidden>​+  * $ 0 <= c <= 1.000$, unde c este costul unui arc. 
 +    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. 
 +    * Dacă după aplicarea algoritmului **nu se găsește drum** pentru o pereche de noduri x șy, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0)
 +    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). 
 +  * timp de execuție 
 +    * C++: 1s 
 +    * Java: 8s
  
-Implementati problema folosind, la alegere, algoritmii lui Prim sau Kruskal, motivând alegerea. 
  
-Ca alternativă la AMA2, puteţi implementa cealaltă variantă de AMA, faţă de cea folosită iniţial.+</​note>​
  
-Posibil fisier de intrare ​(exemplul din laborator): +==== Johnson ==== 
-<code cpp+ 
-# explicatii format ​ +Se dă un graf **orientat** cu **n** noduri. Graful are **costuri oarecare** ​(pot fi și negative)
-n=numar varfuri ​m=numar muchii + 
-# m randuricate unul pentru ​fiecare muchie: start end cost +Se dă lista de adiacență cu costurile aferente, se cere matricea drumurilor minime - **d**, aplicând algoritmul **Johnson**. 
-8 13   + 
-1 2  + 
-1 3 9 +<note warning
-1 4 1 + 
-1 6 7 +Restricții și precizări:​ 
-2 3 12 + 
-2 4 4 +  * $ <1000 $ 
-3 8 13 +  * $ <25000 $ 
-4 5 7 +  * $ -1000 <= c <= 1.000$unde c este costul unui arc. 
-4 6 8 +    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. 
-5 6 3 +    * Dacă după aplicarea algoritmului **nu se găsește drum** ​pentru ​o pereche de noduri x și y, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0). 
-5 7 6 +    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). 
-5 8 5 +    * Dacă graful conține un ciclu de cost negativ, se va afișa mesajul: Ciclu negativ! 
-7 8 2 +  ​* timp de execuție 
-</code>+    * C++: 1s 
 +    * Java: 8s 
 + 
 + 
 +</​note>​ 
 + 
 +==== BONUS ==== 
 + 
 +La acest laborator, asistentul va alege 1-probleme din secțiunea extra. 
 + 
 +==== Extra ==== 
 + 
 +  * [[https://​infoarena.ro/​problema/​rfinv|infoarena/​rfinv]] 
 +  * [[https://​infoarena.ro/​problema/​rf|infoarena/​rf]] 
 +  * [[https://​infoarena.ro/​problema/​coach|infoarena/​coach]] 
 +  * [[https://​codeforces.com/​contest/​295/​problem/​B|codeforces/​greg-and-graph]] 
 +  * [[https://​codeforces.com/​contest/​25/​problem/​C|codeforces/​roads-in-berland]] 
 +  * [[https://​codeforces.com/​problemset/​problem/​21/​D|codeforces/​traveling-graph]] 
 +  * [[https://​codeforces.com/​gym/​101498/​problem/​L|codeforces/​the-shortest-path]] 
 + 
 +===== Referințe ===== 
 + 
 +[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.
  
-<​hidden>​ 
-Pentru un reminder relativ la problema celui de-al doilea AMA, consultati ​ [[http://​webdocs.cs.ualberta.ca/​~xianchen/​teaching/​TutorialW0611.pdf|aceasta pagina]]. 
-</​hidden>​ 
pa/laboratoare/laborator-09.1493680270.txt.gz · Last modified: 2017/05/02 02:11 by andrei_mario.dinu
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