Informatyczny kącik olimpijski
Teleporty
W tej edycji kącika omówimy zadanie pt. Podlewanie ogórków (naprawdę!) z rundy 2A konkursu TopCoder Open 2012.
W zadaniu dany jest pewien zbiór punktów ustawionych wzdłuż prostej
i naszym celem jest odwiedzić wszystkie te punkty w pewnej zadanej kolejności.
Innymi słowy, mamy
różnych punktów ponumerowanych od
do
(porządek numerów niekoniecznie od lewej do prawej)
i powinniśmy odwiedzić je wszystkie w kolejności
Pomóc
nam w tym mogą teleporty – mamy możliwość rozstawić w dowolnych
punktach prostej łącznie
teleportów. Teleporty działają tak, że
w ułamku sekundy możemy teleportować się z dowolnego teleportu do
dowolnego innego teleportu. Naszym zadaniem jest tak ustawić dostępne
teleporty, aby zminimalizować długość trasy odwiedzającej po kolei punkty
przy założeniu, że po drodze możemy teleportować się
dowolnie wiele razy.
Rozwiązywanie każdego (nietrywialnego) zadania warto rozpocząć od
wstępnej analizy problemu, czyli od stwierdzenia, o co w nim tak naprawdę
chodzi. Niech
oznaczają współrzędne kolejnych
punktów od lewej do prawej, a
– numery tych punktów.
Niech wreszcie
oznaczają współrzędne punktów
w porządku zadanych numerów
Zauważmy, że
poszukiwaną optymalną ścieżkę możemy podzielić na fragmenty: od
do
od
do
itd., przy czym każdy
z fragmentów biegnie albo w linii prostej między punktami
a
albo ścieżką od punktu
do najbliższego mu
teleportu i od teleportu najbliższego punktowi
do punktu
Na pewno warto coś zrobić z faktem, że zgodnie z treścią zadania liczba
możliwych rozstawień teleportów jest nieskończona. Naturalna hipoteza jest
taka, że możemy ograniczyć się do teleportów ustawionych w pewnych
spośród zadanych
punktów. Łatwo uzasadnić prawdziwość
tej hipotezy: Załóżmy, że pewien teleport znajdowałby się gdzieś
pomiędzy dwoma sąsiednimi punktami, czyli na pozycji
takiej
że
dla pewnego
Rozważmy wszystkie
momenty, w których korzystamy z teleportu
W każdym
z nich musimy dojść do tego teleportu albo od punktu
(bądź
innego, położonego na lewo), albo od punktu
(bądź innego,
położonego na prawo). To oznacza, że odcinek
przejdziemy
razy, a odcinek
przejdziemy
razy. Jeśli więc
to teleport przestawiamy na pozycję
a w przeciwnym
razie na pozycję
i w obu przypadkach długość trasy nam nie
wzrośnie.
Mamy już jakąś wizję tego, jak będzie wyglądała optymalna trasa oraz
gdzie powinniśmy ustawiać teleporty. To pozwala już skonstruować
pierwsze rozwiązanie zadania, rozpatrujące wszystkie możliwe rozstawienia
teleportów. Złożoność czasowa takiego rozwiązania to z grubsza
Aby poprawić ten rezultat, powinniśmy jakoś „dobrze uchwycić” nasze zadanie. Problem ewidentnie stanowi w nim obecność dwóch porządków: zadanego porządku odwiedzania punktów oraz naturalnej kolejności „od lewej do prawej”, odpowiadającej najkrótszym ścieżkom między kolejnymi punktami. Potrzebujemy jeszcze jakichś spostrzeżeń.
Załóżmy, że znamy pozycje wszystkich rozstawionych teleportów:
Teleporty te wyznaczałyby wówczas podział
ciągu wszystkich punktów na fragmenty położone między kolejnymi
teleportami. Czy na podstawie tego podziału umielibyśmy łatwo obliczyć
wynik?
Rozważmy pewien punkt
taki że
Oznaczmy
Wiemy, że poprzedni punkt w kolejności
odwiedzania znajduje się na pozycji
(punkt ten może też nie
istnieć). Jeśli teraz punkt
szczęśliwym trafem również znajduje się
między teleportami
i
to możemy bardzo łatwo wyznaczyć
najkrótszą ścieżkę między nim a
: albo idziemy bezpośrednio po
prostej, albo korzystamy z kombinacji teleportów
i
A co,
jeśli punkt
znajduje się zupełnie gdzieś indziej? Wiemy wówczas, że
najkrótsza ścieżka od
do
biegnie od
do
najbliższego mu teleportu i dalej od teleportu najbliższego
do
(zauważmy, że jeśli wynikiem jest najkrótsza ścieżka z
do
w linii prostej, to i tak możemy ją opisać w podanej
postaci!). To oznacza, że wkład punktu
do wyniku możemy
opisać całkiem lokalnie: jeśli mianowicie
to do wyniku
dodajemy
![]() | (1) |
a w przeciwnym razie wynik zwiększamy tylko o
![]() | (2) |
natomiast odległość od punktu
do najbliższego mu teleportu
dodamy do wyniku w chwili rozpatrywania tego punktu. Aby ta metoda była
poprawna, przy rozpatrywaniu
należy jeszcze stwierdzić, czy
następny punkt w kolejności odwiedzania,
znajduje się
między teleportami
i
a jeśli nie, jeszcze raz dodać do
wyniku składnik (2). (Jeśli
nie istnieje, nie musimy niczego
dodawać).
To oznacza, że do wyznaczenia wyniku wystarcza nam dla każdego punktu
znajomość pozycji dwóch najbliższych mu teleportów. Możemy więc
zastosować metodę programowania dynamicznego. W ramach stanu
musimy na pewno jakoś zapamiętać zakres rozpatrzonych punktów
wzdłuż prostej, liczbę już ustawionych teleportów oraz pozycje
skrajnych ustawionych teleportów. Najlepiej jest to zrobić tak: pole
tablicy będzie odpowiadać minimalnej sumie wkładów
punktów
do wyniku, przy założeniu, że rozstawiliśmy
wśród nich
teleportów, z których ostatni znajduje się na pozycji
Wówczas ze stanu
mamy
przejść,
do stanów postaci
dla
i możemy je
wszystkie rozpatrzyć w czasie
Żeby uniknąć nieprzyjemnych
przypadków brzegowych, postawimy łącznie
teleporty, z czego
dwa pomocnicze: jeden na pozycji
a drugi na pozycji
Całe rozwiązanie działa w czasie
i pamięci