Ciągi i łańcuchy
Tym razem omówimy dwa zadania z IX International Autumn Tournament in Informatics, który odbył się w listopadzie 2017 roku w Szumem w Bułgarii.
Zadanie (Ciągi). Dane są trzy dodatnie liczby całkowite i
Należy obliczyć liczbę niemalejących ciągów długości
o wartościach będących liczbami całkowitymi z przedziału
w których żadna wartość nie występuje więcej niż
razy. Przykład: dla
i
poprawną odpowiedzią jest
Poprawnymi ciągami są:
oraz
Rozwiązanie
W rozwiązaniu skorzystamy z techniki programowania dynamicznego. Niech oznacza liczbę niemalejących ciągów długości
złożonych z liczb całkowitych z przedziału
w których żadna wartość nie występuje więcej niż
razy. Łatwo zauważyć, że:
dla
jest tylko jeden pusty ciąg;
dla
;
dla
Zastanówmy się teraz, jak obliczyć wartość dla pozostałych par
W ciągach niemalejących elementy o tych samych wartościach tworzą spójny przedział. W szczególności wartości
tworzą spójny przedział długości
Zatem, otrzymujemy ogólny wzór:
![mini,k P[i][ j]=QDP[i−l][ j−1]. D l0](/math/temat/informatyka/algorytmy/2018/05/21/Ciagi_i_lancuchy/19x-d95ea91defa8fa0f7f5e4d29d69b696e486a3aad-dm-33,33,33-FF,FF,FF.gif)
Odpowiedzią w zadaniu jest wartość Rozwiązanie działa w czasie
Rozwiązanie
Powyższy wzór możemy zapisać równoważnie jako:
- dla
- dla
Zauważmy, że obliczenie wartości dla dowolnych
zajmuje czas stały. Zatem całe rozwiązanie działa w czasie
Zadanie (Łańcuchy). Dany jest ciąg liczb całkowitych Łańcuch rozpoczynający się na
-tej pozycji powstaje w niżej opisany sposób. Znajdujemy pierwszy większy element na prawo od
i oznaczamy go przez
Następnie znajdujemy pierwszy większy element na prawo od
i oznaczmy go przez
itd. W ten sposób, dla ustalonego
otrzymujemy łańcuch
W zadaniu należy dla każdego
znaleźć długość łańcucha rozpoczynającego się na
-tej pozycji. Przykład: dla
wynikiem jest
Rozwiązanie
Najprostsze rozwiązanie polega na wygenerowaniu dla każdego łańcucha rozpoczynającego się na
-tej pozycji (zgodnie z opisem w treści zadania) i wypisaniu jego długości. Wyznaczenie łańcucha dla każdej pozycji zajmuje
operacji (musimy przeiterować się po całym ciągu). Zatem całe rozwiązanie działa w czasie
Rozwiązanie
Niech oznacza długość łańcucha rozpoczynającego się na
-tej pozycji. W tym rozwiązaniu będziemy wyznaczali
dla kolejnych
od
do
(od prawej do lewej). Załóżmy, że obliczyliśmy już
i chcemy obliczyć
Jeśli pierwszym większym elementem na prawo od
jest
wtedy
(korzystamy z wcześniej obliczonego wyniku dla l). Jeśli zaś wszystkie elementy na prawo są nie większe niż
wtedy
Zastanówmy się teraz, jak dla każdego wyznaczyć pierwszy większy element na prawo od niego. Oczywiście, możemy to zrobić naiwnie (przeglądając kolejne elementy). Wówczas jednak otrzymamy rozwiązanie
Zauważmy, że dla pary indeksów jeśli
to
nie będzie kolejnym elementem w łańcuchu dla żadnego elementu na pozycji
(możemy myśleć o tym w ten sposób, że
przysłania
). Zatem kandydaci na kolejny element w łańcuchu tworzą ciąg rosnący. Przeglądając ciąg
od prawej do lewej, przechowujemy kandydatów na stosie. Na szczycie stosu znajduje się najmniejszy element, na dole zaś największy. Kiedy chcemy znaleźć kolejny element w łańcuchu dla
wówczas tak długo zdejmujemy elementy ze stosu, aż na szczycie stosu pojawi się element większy niż
Jeśli taki element nie pojawi się, oznacza to, że taki element nie istnieje. Po wyznaczeniu kolejnego elementu w łańcuchu odkładamy
na szczyt stosu.
Powyższy algorytm działa w zamortyzowanym czasie liniowym. Wynika to bezpośrednio z własności stosu (każdy z elementów został dokładnie raz odłożony na stos i raz z niego zdjęty). Powyższe rozwiązanie działa w czasie