Informatyczny kącik olimpijski
Ogromna wieża
Tym razem przyjrzymy się zadaniu Ogromna wieża (ang. A huge tower) pochodzącemu z Olimpiady Informatycznej Europy Środkowej 2010 (CEOI 2010).
Oznaczmy przez 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 , które oznaczają odpowiedź na pytanie „na ile sposobów można zbudować poprawną wieżę z bloków należących do zbioru , tak aby blok o numerze znalazł się na dole?”. Naturalnie, zakładamy, że . Jeśli dostępny jest tylko jeden blok, wieżę można zbudować na dokładnie jeden sposób. Jeśli zaś mamy zbiór co najmniej dwóch bloków, a blok 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 , to pod warunkiem, że , znaleźliśmy sposobów na zbudowanie naszej wieży. Sumujemy takie wartości po wszystkich możliwościach wyboru i otrzymujemy . W ten sposób, szukając odpowiedzi w kolejności od najmniej licznych zbiorów , każdą z nich znajdujemy w czasie proporcjonalnym do rozmiaru , co oznacza, że cały problem zostanie rozwiązany po wykonaniu 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 są uporządkowane niemalejąco, i rozważmy dowolną poprawną wieżę zbudowaną z bloków o numerach od do . Powiedzmy, że w tej wieży blok leży pod blokiem i pomiędzy nie chcemy teraz włożyć blok . Zastanówmy się, kiedy nowa konstrukcja jest poprawna. Poza fragmentem zawierającym bloki , i , nowa wieża jest poprawna wtedy i tylko wtedy, gdy dotychczasowa była. Ponadto, musi być oraz . Pamiętajmy jednak, że , więc druga nierówność zawsze zachodzi. To znaczy, że jeżeli mamy poprawną wieżę zbudowaną z bloków oraz , to wstawiając blok między oraz , otrzymamy poprawną wieżę dla bloków . Z drugiej strony, jeśli mamy poprawną wieżę dla bloków i blok znajduje się w niej między blokami oraz , to . Ponieważ jednak , więc mamy i rozważana wieża po usunięciu bloku staje się poprawną wieżą dla bloków .
Z powyższego rozumowania wyciągamy wniosek, że wieża ze wstawionym pomiędzy bloki i blokiem (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 . Bardzo ważne jest spostrzeżenie, że w tym właśnie wniosku nie używamy tak naprawdę rozmiarów bloków innych niż oraz .
Z takim spostrzeżeniem możemy śmiało konstruować algorytm rozwiązujący nasz problem. Oznaczmy przez liczbę tych bloków spośród , na które można położyć blok . 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 sposób. Używając pierwszych dwóch, na sposobów. I tak dalej: aby utworzyć poprawną wieżę z pierwszych bloków, należy jakoś zbudować wieżę z pierwszych bloków, a następnie na jeden z sposobów ustawić blok . Ostatecznym wynikiem jest więc . Zauważmy jeszcze, że wartości możemy szybko obliczyć, korzystając z (niemalejącego) ciągu . Używamy do tego dwóch indeksów, oraz , przy czym przebiega kolejno wartości od do , a ma być zawsze najmniejszym takim indeksem, że . W takim przypadku mamy, dla każdego kolejno rozpatrywanego , równość . Dodajmy, że oba wskaźniki w trakcie działania tego algorytmu jedynie wzrastają, co zapewnia, że czas obliczenia ciągu jest liniowy. Całkowity koszt czasowy jest więc uzależniony od sortowania rozmiarów bloków i wynosi .