Informatyczny kącik olimpijski
Przeciąganie liny
W tym miesiącu proponujemy zadanie Przeciąganie liny, które pojawiło się w podwarszawskim Józefowie, podczas zeszłorocznej Bałtyckiej Olimpiady Informatycznej. Zadanie opisuje problem optymalizacji znanej wakacyjno-urlopowej zabawy. Co ciekawe, warstwa fabularna proponowanego rozwiązania - choć pozostaje w podobnych klimatach - to jednak odchodzi od liny na rzecz plecaka.

Wierzchołki oznaczono liczbami, krawędzie - literami. Jedyne możliwe przyporządkowania to:
albo
Ale po kolei:
W zadaniu rozważamy osób chcących zabawić się w tytułową grę. Lina do zabawy ma przygotowane
uchwytów, po
z każdej strony. Każda z osób deklaruje zawczasu dwa uchwyty, przy których chciałaby się znaleźć. Znamy również siłę każdej z osób, wyrażoną przez liczbę naturalną od
do
Naszym celem jest ustalić, czy jest możliwe takie ustawienie uczestników zabawy, aby każdy dostał jeden z dwóch wybranych przez siebie uchwytów oraz aby różnica sił między dwiema drużynami nie przekraczała pewnej danej liczby
Ograniczenia opisane w zadaniu wydają się dość dziwaczne. Najlepiej jest na nie spojrzeć w następujący sposób. Rozważmy graf w którym wierzchołki reprezentują uchwyty, a krawędzie - ludzi. Krawędź
łączy wierzchołki
i
wtedy i tylko wtedy, gdy osoba
zadeklarowała jako ulubione właśnie uchwyty
i
W tym języku przyporządkowanie osobom uchwytów sprowadza się do (wzajemnie jednoznacznego) przyporządkowania krawędziom jednego z ich wierzchołków.
Nawet jeśli zapomnimy o warunku dotyczącym zrównoważenia sił obu stron, nie zawsze jest możliwe jakiekolwiek przyporządkowanie spełniające wszystkie preferencje dotyczące uchwytów. Przede wszystkim, w każda spójna składowa o
wierzchołkach musi mieć dokładnie
krawędzi. Takie grafy to tak zwane pseudolasy, a więc zbiory rozłącznych pseudodrzew. Każde pseudodrzewo zawiera dokładnie jeden cykl (dlaczego?). W takim grafie istnieją tylko dwa różne przyporządkowania wierzchołków do sąsiednich krawędzi. Dla wierzchołków nie leżących na cyklu możliwy jest tylko jeden wybór. Na cyklu wybory są dwa: przyporządkujemy krawędzie kolejnym wierzchołkom albo zgodnie albo przeciwnie do ruchu wskazówek zegara.
Wobec powyższego, nasze rozwiązanie w pierwszej fazie sprawdza, czy dane wejściowe rzeczywiście tworzą pseudolas. Jeśli nie, to oczywiście odpowiedź na zadanie brzmi również "nie". Dalej zakładamy więc, że mamy do czynienia już tylko z pewnym zbiorem pseudodrzew. Jak zauważyliśmy wcześniej, dla każdego takiego pseudodrzewa mamy tylko dwie możliwości. Dla każdej z nich obliczmy jaką siłę do lewej strony liny wnosi. Te wartości oznaczmy jako oraz
dla
-tego pseudodrzewa. Bez straty ogólności załóżmy, że zawsze
Widzimy, że łączna siła lewej drużyny będzie równa co najmniej
i co najwyżej
przy czym każdy ze składników
możemy dodać bądź nie, według naszego uznania. Pamiętajmy oczywiście, że chcemy, aby ta siła mieściła się między
a
gdzie
oznacza łączną siłę wszystkich osób.
To ostatnie spojrzenie na problem z zadania opiszemy w języku tak zwanego problemu plecakowego (ang. knapsack problem). Rozważmy plecak o pojemności maksymalnej . Mamy dostępny zbiór przedmiotów (indeksowany
) o wagach
. Pytamy, czy uda się nam zabrać przedmioty o łącznej wadze co najmniej
i jednocześnie nieprzekraczającej pojemności plecaka.
Problem plecakowy to klasyczny problem, który da się standardowo rozwiązać w czasie W naszym zadaniu możemy jednak znaleźć rozwiązania lepsze (o ile
), bo działające w czasie
Skorzystamy z założenia, że siła każdej osoby (a więc i wagi przedmiotów w problemie plecakowym) jest wyrażona liczbą naturalną. Wnioskujemy z tego, że istnieje co najwyżej różnych wartości wag przedmiotów (suma
różnych liczb naturalnych jest równa co najmniej
). Pozostaje ostatni krok, zrealizowany za pomocą programowania dynamicznego:
Tworzymy (początkowo wypełnioną zerami) binarną tablicę rozmiaru
w której będziemy zaznaczać, czy da się przygotować plecak o ustalonej wadze. Działamy w pętli o
obrotach, w każdym obrocie dając dostęp do kolejnej unikatowej wagi przedmiotu.

Oto pseudokod jednego obrotu, dla nowej wagi występującej
razy:
W każdym kroku pętli albo uzyskujemy nową objętość plecaka albo kończymy dodawania dla ustalonej objętości. Oba zdarzenia mogą wystąpić razy, stąd czas działania jednego obrotu pętli to faktycznie
tak, jak zapowiedzieliśmy.