Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Monety

Tomasz Idziaszek

o artykule ...

  • Publikacja w Delcie: luty 2012
  • Publikacja elektroniczna: 01-02-2012

W tym miesiącu opiszemy zadanie Monety, które pojawiło się na Potyczkach Algorytmicznych w roku 2010.

Zadanie. Dany jest ciąg math rzutów monetą. Należy sprawdzić, jaka jest najdłuższa seria kolejnych rzutów, w której wypadło dokładnie math razy więcej orłów niż reszek. Dla przykładu, w ciągu

display-math

serie od piątego do dwunastego, jak również od szóstego do trzynastego rzutu zawierają dokładnie 6 orłów i 2 reszki. Zatem dla math odpowiedzią jest 8, gdyż nie istnieje żadna dłuższa seria zawierająca dokładnie 3 razy więcej orłów niż reszek.

Uprośćmy na chwilę nasze zadanie i powiedzmy, że chcemy znaleźć najdłuższą serię spełniającą warunki zadania, która zawiera pierwszy rzut. Użyjemy dwóch zmiennych math i math oznaczających liczby reszek i orłów, które wypadły do tej pory. Za każdym razem, kiedy po rzucie wystąpi math będzie to oznaczało, że znaleźliśmy dłuższą serię. Zauważmy jednak, że nie potrzebujemy aż dwóch zmiennych: nie interesuje nas dokładna liczba orłów i reszek, a jedynie ich wzajemne zbilansowanie. Możemy więc trzymać jeden licznik math który po każdym rzucie będziemy uaktualniali następująco: wyrzucenie reszki zwiększa math o math natomiast wyrzucenie orła zmniejsza math o math Oznaczmy przez math wartość licznika po math-tym rzucie (przyjmujemy też math). Teraz każde math odpowiada poprawnej serii kończącej się math-tym rzutem.

A co w ogólnym przypadku? Zastanówmy się, jaki warunek musi być spełniony, aby pewna seria spełniająca warunki zadania kończyła się math-tym rzutem monetą. Łatwo przekonać się, że jest tak wtedy, gdy istnieje taki rzut math że wartość licznika po math-tym rzucie jest taka sama jak po math-tym rzucie ( math) – wtedy rzuty od math-ego do  math-tego tworzą poprawną serię. Chcielibyśmy zatem dla danego math szybko stwierdzać, jaki jest (o ile istnieje) najmniejszy numer rzutu math dla którego math Skorzystamy w tym celu z drzewa wyszukiwań binarnych, w którym dla klucza math będziemy trzymać odpowiadający mu numer rzutu. Teraz po math-tym rzucie, jeśli klucz math występuje w drzewie z wartością math to zgłaszamy znalezienie serii o długości  math W przeciwnym przypadku uaktualniamy drzewo, wstawiając do niego klucz math z wartością math Zauważmy, że jeśli w drzewie istniał już klucz math to nie musieliśmy go uaktualniać, gdyż interesuje nas jedynie najmniejszy numer rzutu odpowiadający temu kluczowi. Każda operacja na drzewie zajmuje czas math  zatem całe rozwiązanie działa w czasie math

Czytelnicy, którzy słyszeli o tablicach haszujących, mogą powiedzieć, że bez kłopotu uzyskalibyśmy rozwiązanie działające w oczekiwanym czasie math  gdybyśmy zamiast drzewa wyszukiwań binarnych użyli właśnie tablicy haszującej. To prawda, ale okazuje się, że można jeszcze lepiej – wykorzystując specyfikę naszego zadania, można zaprojektować „tablicę haszującą”, która da nam rozwiązanie math  i to w pesymistycznym przypadku!

Zamiast drzewa będziemy mieli tablicę math Odwołania do klucza math będą realizowane przez komórkę math w której będziemy trzymać listę par postaci (klucz math numer rzutu). Innymi słowy, po math-tym rzucie sprawdzamy, czy na liście math znajduje się para math dla pewnego math Jeśli tak, to zgłaszamy znalezienie serii o długości  math a w przeciwnym przypadku uaktualniamy listę math wstawiając do niej nową parę math

I teraz czas na kluczową obserwację. Załóżmy, że nastąpił drugi z powyższych przypadków. To oznacza, że na liście math mogą znajdować się tylko pary math gdzie math dla math Zauważmy jednak, że ilekroć nasz licznik osiągnął pewną wartość math to w przyszłości już nigdy nie będzie równy math lub mniejszy, ponieważ po każdym rzucie możemy go zmniejszyć co najwyżej o math Zatem na liście nie ma żadnej pary dla math (bo wtedy byłoby math co przeczy temu, że aktualna wartość licznika to math). Co więcej, skoro pary, które mogą pojawić się na liście, spełniają math to sytuacja, w której licznik byłby równy math nigdy już nie wystąpi. Tak więc przed dodaniem pary math do listy math możemy z niej usunąć wszystkie występujące na niej pary. Widać zatem, że na żadnej liście nie będzie nigdy więcej niż jednej pary – stąd czas wyszukiwania na liście faktycznie będzie stały.