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:
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