Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Słownik przekątnych

Filip Wolski

o artykule ...

  • Publikacja w Delcie: lipiec 2008
  • Publikacja elektroniczna: 20-12-2010

W tym odcinku zajmiemy się zadaniem, które zamiast na zawodach, pojawiło się na... kolokwium (z Algorytmów i Struktur Danych). Stanowiłoby ono bardzo ładne zadanie na zawodach.

obrazek

Rys. 1 Kilka przekątnych ośmiokąta reprezentowanych jako łuki. Wartością math jest łuk math

Rys. 1 Kilka przekątnych ośmiokąta reprezentowanych jako łuki. Wartością math jest łuk math

obrazek

Rys. 2 Tak wyobrażamy sobie drzewo przedziałowe.

Rys. 2 Tak wyobrażamy sobie drzewo przedziałowe.

Od czego zacząć? Na początek może ułatwimy sobie zadanie — zauważmy, że wielokąt z zadania można „wygiąć” na płaszczyźnie, aż wierzchołki utworzą ciąg punktów na prostej, ponumerowanych od math do math, a przekątne zamienią się na łuki (jak na rysunku 1). Resztę zadania łatwo przetłumaczyć do tej nowej wersji. W tym momencie już pojawia się pomysł — może uda się użyć drzewa przedziałowego? Ale zanim napiszemy jak, wyjaśnijmy sobie, co będziemy rozumieć przez drzewo przedziałowe.

Drzewo przedziałowe (rys. 2) służy do przechowywania informacji związanych z podprzedziałami zadanego przedziału math. W korzeniu drzewa jest zapisana informacja związana z całym przedziałem, w jego dwóch synach informacje związane z lewą i prawą połową tego przedziału i tak dalej, aż wreszcie w liściach zapamiętywane są wartości odpowiadające pojedynczym punktom. Wartość przechowywaną w wierzchołku math będziemy oznaczać math

obrazek

Rys. 3 Zawartość drzewa przedziałowego po wstawieniu przekątnych z rysunku 1.

Rys. 3 Zawartość drzewa przedziałowego po wstawieniu przekątnych z rysunku 1.

Jako ćwiczenie Czytelnik może w oparciu o drzewo przedziałowe skonstruować strukturę danych do obsługi dynamicznie zmieniającego się ciągu math, dopuszczającą operacje (1) inicjuj wszystkie wyrazy na math, (2) ustaw math oraz (3) podaj sumę podciągu math. Operacje (2) i (3) powinny działać w czasie math.

No dobrze, ale jak to zastosować do naszego problemu?

Zapytajmy, kiedy w nowym modelu (tym z prostą), można połączyć dwa punkty mathmath przekątną? Spójrzmy na rysunek 1. Dla danego math niech math oznacza najniższy łuk, który przecina pionowa półprosta wychodząca z punktu math (dotknięcie nie liczy się jako przecięcie; wartością math może być „pusty” łuk). Łuk od math do math można dodać dokładnie wtedy, gdy math. Musimy więc szybko obliczać wartości funkcji math. Do tego przyda się drzewo przedziałowe.

Przypuśćmy, że wiemy już, iż możemy dodać przekątną math. Jak to zrobić? Otóż przekątna „pokryje” podciąg wierzchołków math. Słowo pokryje oznacza, że półprosta wychodząca z każdego z wierzchołków z tego przedziału przetnie naszą przekątną, choć niekoniecznie jako pierwszą.

Funkcja dodająca przekątną math będzie działać rekurencyjnie. Najpierw wywołujemy ją dla korzenia. Dodając przekątną math w wierzchołku math drzewa przedziałowego, wykonujemy operacje:

jeśli podprzedział z wierzchołka math zawiera się w  math, to ustaw math na niższy z łuków mathmath (wysokość łuku można szybko obliczyć, jest to math),
jeśli podprzedział z wierzchołka math jest rozłączny z  math, to nic nie rób,
w przeciwnym razie wywołaj funkcję math dla dzieci wierzchołka math

Łatwo zauważyć, że math to najniższy spośród łuków zapamiętanych w wierzchołkach drzewa odpowiadających przedziałom pokrywającym punkt math, czyli na ścieżce prowadzącej z korzenia do wierzchołka math. Zatem math można znaleźć w czasie math.

Funkcja Sprawdź może działać na podobnej zasadzie, sprawdzając, czy w odwiedzanych po drodze wierzchołkach jest zapamiętana poszukiwana przekątna. (Każda wstawiona przekątna zostaje gdzieś zapamiętana, dlaczego?)

Aktualizację stanu struktury podczas usuwania przekątnej pozostawiamy Czytelnikowi.