--- title: L1Z5 AISD --- ## Algorytmy i struktury danych ### Lista 1, zadanie 5 ==Marek Padlewski, 299778== :::info Ułóż algorytm, który dla zadanego acyklicznego grafu skierowanego $G$ znajduje długość najdłuższej drogi w $G$. Następnie zmodyfikuj swój algorytm tak, by wypisywał drogi o największej długości (jeśli jest kilka takich dróg, to Twój algorytm powinien wypisać dowolną z nich). ::: #### Idea 1. Wykonajmy sortowanie topologiczne grafu $G$. Wynik sorotwania zapiszmy w tablicy `T[]`. 2. Stwórzmy tablicę `D[]` w której dla każdego wierzchołka $v \in G$ będziemy przechowywać wartość równą aktualnie najdłuższej drodze do $v$. Na początku wartości te będą równe zero. 3. Przejdźmy tablicę `T[]` i dla każdego wierzchołka $v$, przechodząc po wszystkich $u$, gdzie $u$ należy do zbioru wierzchołków, do których istnieje krawędź wychodząca z $v$, aktualizujmy wartość zamieszczoną w `D[u]`. $D[u] = max(D[u], D[v] + 1)$. Jako że bierzemy zawsze maksimum z wpisanej wcześniej wartości lub z wartości powstałej poprzez dodanie 1 do wartości znajdującej się w poprzednim wierzchołku na drodze, da nam to na koniec maksymalną możliwą drogę. 4. Przejdźmy po tablicy `D[]` i wypiszmy $max(D[i])$, czyli naszą długość drogi o największej długości. ##### Algorytm ```cpp int findMaxLength(){ unsigned int n = number_of_vertices; T[n] = topological_sort(G); int D[n]; //ustawiamy początkową wartość w D[] na 0 for (int i = 0 ; i < n ; i++) D[i] = 0; for (auto v : T){ for (auto u : neighbours[v]){ D[u] = max(D[u], D[v] + 1); } } int result = 0; for (auto value : D) result = max(result, value); return result; } ``` #### Modyfikacja 1. Zamiast przechowywać w tablicy `D[]` jedynie wartości aktualnie najdłuższej drogi wchodzącej do danego wierzchołka, możemy przechowywać parę `p`, w której `p.first` będzie równy aktualnie najdłuższej drodze do $v$, a `p.second` będzie równy $id$ wierzchołka z którego przyszliśmy do $v$. Na początku wartości `p.first` będą równe zero. 2. Następnie wystarczy zaczynając od wierzchołka z największą zapisaną długością drogi, odwzorować kolejne kroki, przechodząc od jednego wierzchołka do drugiego. ##### Algorytm zmodyfikowany ```cpp void find(){ unsigned int n = number_of_vertices; T[n] = topological_sort(G); int D[n]; //ustawiamy początkową wartość w D[] na 0 for (int i = 0 ; i < n ; i++) D[i].first = 0; for (auto v : T){ for (auto u : neighbours[v]){ if (v.first + 1 > u.first){ u.first = v.first + 1; u.second = v; //przypisujemy id wierzchołka v } } } int result = 0; for (auto value : D) result = max(result, value); for (int i = 0 ; i < n ; i++){ if(D[i].first == result){ //wywołujemy funkcję wypisującą drogę //od wierzchołka z największą wartością print(i); break; } } } void print(int i){ if (D[i].second == null) //gdy dojdziemy do końca drogi std::cout << i << " "; else //wypisanie drogi w odpowiedniej kolejności std::cout << print(D[i].second) << " " << i; } ``` #### Złożonośc czasowa * Sortowanie topologiczne: $O(n + m)$ * Przejście po wszystkich wierzchołkach i ich sąsiadach: $O(n + m)$ * Przejście po tablicy `D[]` w poszukiwaniu wyniku oraz wypisanie drogi: $O(n)$ #### Złożonośc pamięciowa * Wszystkie tablice są rozmiaru adekwatnego do liczby wierzchołków w $G$, tak więc: $O(n)$