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