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
.