Autostrady
02.06.2009 - Bartosz Walczak
![]() ![]() ![]() Jednym z moich ulubionych zadań z konkursów informatycznych są ,,Autostrady'' z II etapu X Olimpiady Informatycznej 2002/2003. Jego treść (obrana z historyjki) wygląda mniej więcej tak:
Na prostej wybrano Zadanie to ma bardzo prostą interpretację grafową. Wierzchołkami grafu są pary punktów, które chcemy połączyć łukiem (w dalszej części będę je nazywał po prostu łukami). Dwa wierzchołki są połączone krawędzią, gdy odpowiadające im łuki umieszczone po tej samej stronie prostej przecinają się. Odpowiednie rozmieszczenie łuków sprowadza się do pokolorowania wierzchołków tego grafu dwoma kolorami w taki sposób, aby wierzchołki tego samego koloru nie były połączone krawędzią (takie kolorowanie nazywamy poprawnym). Wówczas jeden kolor wskazuje, które łuki poprowadzić po jednej stronie, a drugi kolor -- po drugiej stronie prostej. Pojawia się jednak istotny problem: taki graf może mieć rozmiar kwadratowy. Ograniczenia na rozmiar danych wejściowych jasno wskazują, że złożoność kwadratowa jest w tym zadaniu niewystarczająca. Czy to oznacza, że musimy zrezygnować z podejścia grafowego? Bynajmniej. Naszym celem jest poprawne pokolorowanie grafu przecięć dwoma kolorami. Takie kolorowanie, o ile istnieje, jest zdeterminowane przez jakikolwiek spójny podgraf rozpinający (tj. posiadający ten sam zbiór wierzchołków), na przykład drzewo. Jeżeli graf przecięć jest niespójny, to dwukolorowanie jest zdeterminowane na każdej spójnej składowej przez drzewo rozpinające tę składową. Poprawne dwukolorowanie drzewa rozpinającego niekoniecznie jest poprawnym dwukolorowaniem całej składowej, ale jeżeli nie jest, to składowa ta w ogóle nie jest dwukolorowalna. Jak zatem wygląda algorytm? Zamiast pełnego grafu przecięć, znajdujemy las drzew rozpinających jego spójne składowe (w skrócie: las przecięć). Następnie kolorujemy go poprawnie dwoma kolorami, po czym sprawdzamy, czy otrzymane kolorowanie jest poprawnym kolorowaniem całego grafu, a więc czy łuki poprowadzone po odpowiednich stronach prostej danych przez kolory wierzchołków rzeczywiście się nie przecinają. Takie sprawdzenie jest już łatwe -- analogiczne do sprawdzenia poprawności wyrażenia nawiasowego. Jedyną kwestią, która pozostała do rozwiązania, jest szybkie znajdowanie lasu przecięć. Rozwiązanie wzorcowe z OI dodaje nowe łuki do lasu przecięć w dowolnej kolejności, używając do tego celu zrównoważonego drzewa przeszukiwań binarnych wzbogaconego o pewne dodatkowe informacje, przez co jest dość skomplikowane i trudne w implementacji. Ja rozumowałem następująco: skoro wszystkie łuki są znane od początku, to możemy je wstępnie posortować, co powinno uprościć dalszą część algorytmu. Sortujemy więc łuki w kolejności rosnących lewych końców, a w przypadku równości -- malejących prawych końców, a następnie w tej kolejności dodajemy je do konstruowanego lasu przecięć.
Aktualnie rozpatrywany i dodawany do lasu przecięć łuk nazwijmy nowym łukiem. Niech
,,Interesujące'' składowe (te, które sięgają poza
Zobaczmy, co się dokładnie dzieje ze składowymi w powyższym algorytmie. W każdej składowej znajdujemy jej reprezentanta (łuk o najmniejszym prawym końcu), czasem (kiedy Najprostszą implementacją kolejki złączalnej jest drzewo lewicowe. W drzewie tym wartości przechowywane w węzłach spełniają warunek kopca (wartość w węźle jest nie większa niż wartości w jego dzieciach), a ponadto w każdym poddrzewie skrajnie prawa ścieżka jest najkrótsza ze wszystkich ścieżek od korzenia do liścia zewnętrznego (jest to umowny węzeł, który występuje wszędzie tam, gdzie nie ma syna). Ta ostatnia własność implikuje, że ścieżka ta ma logarytmiczną długość. Łączenie dwóch takich drzew sprowadza się do scalenia ich skrajnie prawych ścieżek z zachowaniem warunku kopca, a następnie przywrócenia ,,lewicowości'' poprzez zamianę miejscami synów w pewnych węzłach tej (scalonej) ścieżki -- wszystko odbywa się w czasie logarytmicznym. Element minimalny oczywiście leży w korzeniu drzewa. Jego usunięcie sprowadza się do połączenia poddrzew obu jego synów.
Poniżej załączam pełny kod mojego rozwiązania. Algorytm działa w czasie #include <iostream>
(4 ocen) |
Copyright © 2008-2010 Wrocławski Portal Informatyczny
design: rafalpolito.com