Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Godzilla

Tomasz Idziaszek

o artykule ...

  • Publikacja w Delcie: grudzień 2011
  • Publikacja elektroniczna: 01-12-2011

W tym miesiącu omówimy zadanie, które rozwiązywali uczestnicy Obozu Naukowo-Treningowego im. A. Kreczmara w 2009 r.

obrazek

Zadanie 1. Sieć telewizji kablowej składa się z math węzłów i math  jednokierunkowych połączeń. Do każdego węzła sieci jest podłączony co najmniej jeden dom. Niektóre węzły są wyróżnione i do nich bezpośrednio transmitowany jest program. W danym domu można go oglądać, jeśli istnieje połączenie (niekoniecznie bezpośrednie) od wyróżnionego węzła do węzła, do którego podłączony jest dom.

Niestety, sieć jest narażona na ataki złośliwej Godzilli, która, nie wiedzieć czemu, żywi się infrastrukturą telewizji kablowej. Co dzień zjada ona jedno połączenie z sieci. Mając daną listę math połączeń, które kolejno zje Godzilla, należy wyznaczyć, ile minimalnie węzłów należy oznaczyć jako wyróżnione każdego dnia, tak aby każdy dom mógł odbierać program.

Sieć kablową możemy traktować jako graf skierowany math  o math wierzchołkach i math  krawędziach. Aby znaleźć minimalny zbiór wyróżnionych węzłów, wyznaczamy podział grafu math  na silnie spójne składowe. Silnie spójną składową nazwiemy początkową, jeśli nie wchodzi do niej żadna krawędź (tzn. do żadnego wierzchołka tej składowej nie wchodzi krawędź z wierzchołka spoza tej składowej). Zauważmy, że w każdej początkowej składowej musimy wyróżnić co najmniej jeden wierzchołek, aby dostarczyć program do wierzchołków tej składowej. A ponieważ do każdej niepoczątkowej składowej istnieje ścieżka ze składowej początkowej, więc taki zbiór wierzchołków jest wystarczający.

Znajdowanie podziału na silnie spójne składowe możemy zrealizować dwukrotnym przejściem grafu w głąb w czasie math  Jeśli po każdym usunięciu krawędzi będziemy wyznaczali ten podział od nowa, dostaniemy algorytm o koszcie czasowym math

Chcielibyśmy umieć szybko uaktualniać strukturę składowych po usunięciu krawędzi. Nie jest jednak jasne, jak to zrobić, gdyż po takiej operacji jedna ze składowych może rozpaść się na mniejsze składowe i wydaje się, że nie da się ich wyznaczyć szybciej niż w czasie liniowym względem rozmiaru oryginalnej składowej. Zastosujemy więc pomysłową sztuczkę i odwrócimy czas: zaczniemy od grafu, w którym usunięto wszystkie math krawędzi, i będziemy je dodawać do grafu w odwrotnej kolejności. Tym sposobem pojedynczą operacją będzie połączenie kilku składowych w jedną, a to już wygląda przyjaźniej.

Niech math będzie aktualną liczbą początkowych składowych. Przez cały czas będziemy utrzymywać podział zbioru wierzchołków grafu

display-math

Zbiór math  tworzą wierzchołki math-tej początkowej składowej; do zbioru math należą zaś wierzchołki, które są osiągalne z math  (jeśli wierzchołek jest osiągalny z kilku początkowych składowych, to należy do zbioru math dla jednej z tych składowych).

Zobaczmy, co się dzieje, gdy dodajemy do grafu nową krawędź math Jeśli math dla pewnego math to dodanie tej krawędzi nie spowoduje żadnych zmian w strukturze początkowych składowych, a więc nie musimy nic robić.

W przeciwnym przypadku math  dla pewnego math Będziemy przeszukiwać graf transponowany math (czyli przechodzić krawędzie grafu math  w odwrotnym kierunku), np. algorytmem DFS, począwszy od wierzchołka math Dla każdego napotkanego wierzchołka math  ze zbioru math przenosimy go do zbioru math  (wiemy, że jest cykl math  gdyż math  jest osiągalny z math ). Oczywiście, dla każdego napotkanego wierzchołka math  ucinamy przeszukiwanie, gdyż gdy raz wejdziemy do math  to już z niego nie wyjdziemy.

Jeśli zaś napotkamy wierzchołek math  dla math to znaczy, że znaleźliśmy ścieżkę, która pokazuje, iż składowa math  stała się osiągalna ze składowej math  Zatem powiększamy math wykonując math  usuwamy zbiory math  i kończymy przeszukiwanie. Liczba początkowych składowych zmalała o jeden.

Oszacujmy złożoność czasową naszego algorytmu. Obliczenie podziału dla początkowego grafu math  bez math zjedzonych krawędzi realizujemy w czasie math  Podział ten będziemy przechowywać w strukturze danych dla zbiorów rozłącznych (ang. find-union). Wtedy każda operacja 1) zapytania, do którego ze zbiorów należy dany wierzchołek i  2) złączenia zbiorów będzie miała „prawie stały” koszt zamortyzowany – a dokładniej math  przy czym w praktyce math Dodatkowo, sumaryczny czas przeszukiwania grafu math  wynosi math  gdyż każdą krawędzią przechodzimy co najwyżej raz.

Zatem całkowity koszt czasowy to math  przy zużyciu pamięci rzędu math