Informatyczny kącik olimpijski
Bitoniczny komiwojażer
Tym razem w kąciku lekko zmodyfikowana wersja zadania Listonosz z konkursu Wielka Przesmycka 2004.
W klasycznym problemie komiwojażera mamy dany graf nieskierowany w którym każda krawędź ma przypisaną pewną nieujemną, całkowitą wagę i poszukujemy najlżejszego cyklu przechodzącego przez każdy wierzchołek grafu dokładnie raz. Ten problem jest NP-trudny, co znaczy, że nie należy spodziewać się, że uda się go rozwiązać w czasie wielomianowym (przynajmniej nie w czasie zawodów). W bitonicznej wersji problemu komiwojażera wprowadzamy jedno dodatkowe wymaganie dotyczące cyklu. Otóż przyjmujemy, że wierzchołki grafu są ponumerowane od 1 do , i poszukujemy najlżejszego cyklu przechodzącego raz przez każdy wierzchołek grafu, w którym ciąg numerów wierzchołków jest bitoniczny, czyli najpierw rosnący, a potem malejący (przy pewnym wyborze wierzchołka startowego cyklu). Jak nietrudno się domyślić, wymaganie, by cykl był bitoniczny, istotnie upraszcza podany problem.
Nazwijmy interesujące nas w tym zadaniu cykle cyklami bitonicznymi. Zacznijmy od spostrzeżenia, że wierzchołek numer 1 na pewno znajduje się na początku bądź na końcu każdego cyklu bitonicznego. W drugim z tych przypadków możemy przenieść ten wierzchołek na początek cyklu. To oznacza, że dowolny cykl bitoniczny zaczyna się w wierzchołku 1, prowadzi pewną ścieżką o rosnących numerach wierzchołków aż do wierzchołka po czym wraca do wierzchołka 1, idąc po malejących numerach wierzchołków. Na cykl takiej postaci możemy też spojrzeć nieco inaczej: z wierzchołka 1 prowadzimy dwie rozłączne ścieżki o rosnących numerach wierzchołków, które na końcu spotykają się w wierzchołku Ścieżki o rosnących numerach wierzchołków będziemy dalej nazywali po prostu ścieżkami rosnącymi.
Takie spojrzenie na nasz problem pozwala uzyskać pierwsze rozwiązanie wielomianowe. Zastosujemy metodę programowania dynamicznego. Dla każdej pary wierzchołków takich że przez oznaczmy sumę wag w najlżejszej parze ścieżek rosnących zaczynających się w wierzchołku 1, kończących się odpowiednio w wierzchołkach oraz oraz przechodzących przez każdy wierzchołek – poza wierzchołkiem 1 i, ewentualnie, wierzchołkiem – co najwyżej raz. Po chwili namysłu zauważamy, że aby dało się te wartości obliczać, na musimy nałożyć jeszcze jeden dodatkowy warunek: nasze dwie ścieżki rosnące muszą przechodzić przez wszystkie wierzchołki ze zbioru Teraz jest już całkiem prosto opisać przejścia ze stanu Naszą parę ścieżek rosnących możemy rozszerzyć za pomocą krawędzi wychodzącej z wierzchołka albo z wierzchołka Aby był spełniony nasz dodatkowy warunek, dodana krawędź musi prowadzić do wierzchołka W pierwszym przypadku aktualizujemy wartość (za pomocą ), a w drugim – wartość (tym razem za pomocą ). Jeśli jednak to mamy tylko jedno przejście, do stanu (za pomocą ). Zaczynamy, oczywiście, od stanu a kończymy w stanie Jeśli graf będziemy reprezentować za pomocą tablicy sąsiedztwa (czyli funkcję wagi zapiszemy w tablicy dwuwymiarowej), otrzymamy algorytm o złożoności czasowej
Może wydawać się nieco zaskakujące, że istnieje bardziej efektywne rozwiązanie naszego problemu. Aby je dostrzec, należy przyjrzeć się dokładniej strukturze cyklu bitonicznego.
Załóżmy, że gdzieś na tym cyklu, na jednej ze ścieżek rosnących, leży jakaś „długa” krawędź czyli taka, że Załóżmy na razie, że i Wówczas możemy od razu powiedzieć coś o drugiej ścieżce rosnącej: mamy na niej jakąś krawędź dla dalej znajduje się sekwencja krawędzi „jednostkowych” wreszcie z wierzchołka wychodzi krawędź prowadząca do jakiegoś wierzchołka patrz rysunek poniżej.
Powyższe spostrzeżenie pozwala uprościć postać stanu w naszym programowaniu dynamicznym. Otóż wystarczy nam tylko jeden parametr: stan będzie reprezentować najlżejszą parę ścieżek rosnących, z których jedna kończy się w wierzchołku i wchodzi tam długą krawędzią, natomiast druga kończy się w wierzchołku Oczywiście, ścieżki muszą być rozłączne, pomijając wierzchołek 1, oraz muszą razem zawierać wszystkie wierzchołki z zakresu Przejścia pomiędzy stanami są wyznaczone przez długie krawędzie. Dokładniej, długa krawędź z wierzchołka do wierzchołka generuje przejście ze stanu do stanu jeśli tylko istnieje sekwencja krawędzi jednostkowych prowadzących z wierzchołka do wierzchołka
W tym algorytmie będziemy mieli co najwyżej tyle przejść między stanami, ile jest długich krawędzi w grafie, a jedyne wyzwanie polega na tym, żeby efektywnie symulować te przejścia. To już nie będzie trudne, będziemy to wykonywać w czasie stałym. Musimy jedynie umieć sprawdzać, czy dwa zadane wierzchołki są połączone sekwencją krawędzi jednostkowych, a jeśli tak, to jaka jest suma wag tych krawędzi. Do tego celu wystarczą nam ciągi sum częściowych dwóch ciągów: ciągu oznaczającego istnienie krawędzi (jedynka, jeśli krawędź istnieje, zero, jeśli nie istnieje) oraz ciągu Oznaczmy przez i tablice reprezentujące odpowiednie ciągi sum częściowych (czyli i analogicznie dla ). Tablice te łatwo obliczamy w czasie Wówczas przejście między stanami i za pomocą długiej krawędzi jest możliwe, gdy a waga tego przejścia to
Ustalenie stanów początkowych i końcowych w usprawnionym programowaniu dynamicznym pozostawiamy Czytelnikowi. Całe rozwiązanie działa w czasie a zatem liniowo od rozmiaru wejścia.