Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Ogromna wieża

Tomasz Kulczyński

o artykule ...

  • Publikacja w Delcie: styczeń 2011
  • Publikacja elektroniczna: 20-12-2010

Tym razem przyjrzymy się zadaniu Ogromna wieża (ang. A huge tower) pochodzącemu z Olimpiady Informatycznej Europy Środkowej 2010 (CEOI 2010).

obrazek
obrazek

Oznaczmy przez math rozmiary poszczególnych bloków, a wieżę zbudowaną z nich zgodnie z warunkami zadania nazwijmy poprawną. Pierwsze rozwiązanie, które przedstawimy, jest najmniej efektywne, ale nie wymaga żadnych spostrzeżeń. Wystarczy użyć programowania dynamicznego do stopniowego obliczania wartości math, które oznaczają odpowiedź na pytanie „na ile sposobów można zbudować poprawną wieżę z bloków należących do zbioru math, tak aby blok o numerze  math znalazł się na dole?”. Naturalnie, zakładamy, że math. Jeśli dostępny jest tylko jeden blok, wieżę można zbudować na dokładnie jeden sposób. Jeśli zaś mamy zbiór  math co najmniej dwóch bloków, a blok  math ma znaleźć się na dole, to należy na wszystkie sposoby wybrać blok, który ma się znaleźć bezpośrednio na nim. Jeśli ten przedostatni blok oznaczymy przez  math, to pod warunkiem, że math, znaleźliśmy math sposobów na zbudowanie naszej wieży. Sumujemy takie wartości po wszystkich możliwościach wyboru math i otrzymujemy math. W ten sposób, szukając odpowiedzi w kolejności od najmniej licznych zbiorów  math, każdą z nich znajdujemy w czasie proporcjonalnym do rozmiaru  math, co oznacza, że cały problem zostanie rozwiązany po wykonaniu math operacji.

Aby znaleźć bardziej efektywne rozwiązanie, warto inaczej pomyśleć o budowaniu wieży. Zamiast zastanawiać się, który z bloków położymy na spód albo wierzch, rozpatrujmy bloki kolejno, od najmniejszego do największego, ale za to dopuśćmy umieszczanie kolejnego bloku gdzieś w środku już zbudowanej konstrukcji. Załóżmy, że rozmiary bloków  math są uporządkowane niemalejąco, i rozważmy dowolną poprawną wieżę zbudowaną z bloków o numerach od  math do  math. Powiedzmy, że w tej wieży blok  math leży pod blokiem  math i pomiędzy nie chcemy teraz włożyć blok  math. Zastanówmy się, kiedy nowa konstrukcja jest poprawna. Poza fragmentem zawierającym bloki mathmath i  math, nowa wieża jest poprawna wtedy i tylko wtedy, gdy dotychczasowa była. Ponadto, musi być math oraz math. Pamiętajmy jednak, że math, więc druga nierówność zawsze zachodzi. To znaczy, że jeżeli mamy poprawną wieżę zbudowaną z bloków math oraz math, to wstawiając blok  math między math oraz  math, otrzymamy poprawną wieżę dla bloków math. Z drugiej strony, jeśli mamy poprawną wieżę dla bloków math i blok  math znajduje się w niej między blokami math oraz  math, to math. Ponieważ jednak math, więc mamy math i rozważana wieża po usunięciu bloku  math staje się poprawną wieżą dla bloków  math.

Z powyższego rozumowania wyciągamy wniosek, że wieża ze wstawionym pomiędzy bloki math i  math blokiem  math (nie mniejszym od wszystkich bloków wieży) jest poprawna wtedy i tylko wtedy, gdy była poprawna bez niego oraz gdy można go położyć na bloku  math. Bardzo ważne jest spostrzeżenie, że w tym właśnie wniosku nie używamy tak naprawdę rozmiarów bloków innych niż math oraz  math.

Z takim spostrzeżeniem możemy śmiało konstruować algorytm rozwiązujący nasz problem. Oznaczmy przez math liczbę tych bloków spośród math, na które można położyć blok  math. Każdy blok można też położyć na samym dole wieży. Widzimy, że używając tylko pierwszego bloku, poprawną wieżę można zbudować na math sposób. Używając pierwszych dwóch, na math sposobów. I tak dalej: aby utworzyć poprawną wieżę z  math pierwszych bloków, należy jakoś zbudować wieżę z pierwszych math bloków, a następnie na jeden z  math sposobów ustawić blok  math. Ostatecznym wynikiem jest więc math. Zauważmy jeszcze, że wartości  math możemy szybko obliczyć, korzystając z (niemalejącego) ciągu  math. Używamy do tego dwóch indeksów, math oraz  math, przy czym math przebiega kolejno wartości od  math do  math, a  math ma być zawsze najmniejszym takim indeksem, że math. W takim przypadku mamy, dla każdego kolejno rozpatrywanego  math, równość math. Dodajmy, że oba wskaźniki w trakcie działania tego algorytmu jedynie wzrastają, co zapewnia, że czas obliczenia ciągu  math jest liniowy. Całkowity koszt czasowy jest więc uzależniony od sortowania rozmiarów bloków i wynosi  math .