Informatyczny kącik olimpijski
Kuglarz
W tym miesiącu omówimy zadanie Kuglarz z pierwszej rundy Potyczek Algorytmicznych 2014. Tytułowy kuglarz zaprasza przechodniów do następującej gry...
Zadanie 1. Na stoliku w rzędzie ustawił kubków z numerami
a zawczasu pod niektórymi schował kauczukowe kulki. Jeśli grający dokładnie odgadnie, które to kubki, to dostaje nagrodę. Kuglarz odpłatnie udziela grającemu podpowiedzi. Za
bajtogroszy (dla
) gotów jest zdradzić, jaka jest parzystość liczby kulek schowanych pod kubkami o kolejnych numerach
Znając ceny wszystkich możliwych podpowiedzi, należy wyznaczyć koszt zebrania informacji, które pozwolą określić z całą pewnością, pod którymi kubkami znajdują się kulki. Ściślej rzecz biorąc, należy znaleźć najmniejszą taką liczbę
że istnieje strategia zadawania pytań, która niezależnie od odpowiedzi kuglarza pozwala na zlokalizowanie kulek za co najwyżej
bajtogroszy.

Rys. 1 Przykładowa tabela kosztów dla kubków. Koszt
pytania
znajduje się na przecięciu wiersza
z kolumną

Spróbujmy wyznaczyć rozwiązanie dla tabelki kosztów z rysunku 1. Naiwne zadanie pytań o jednokubkowe przedziały kosztowałoby bajtogroszy. Czy da się lepiej? Oznaczmy przez
podpowiedź na temat parzystości liczby kulek pod kubkami o numerach
Zacznijmy od pytań o małych kosztach: pytanie
daje nam informację, czy pod pierwszym kubkiem znajduje się kulka, a pytanie
o parzystość liczby wszystkich kulek. Zadanie ich kosztuje
bajtogrosze. Następne pytanie o małym koszcie to
ale czy rzeczywiście opłaca się je zadawać? Nie, gdyż na podstawie poprzednich podpowiedzi, znamy parzystość kulek na pozycjach od 2 do 5.
Tę obserwację można nieco uogólnić: na podstawie odpowiedzi na dwa dowolne pytania z trójki oraz
jesteśmy w stanie wyznaczyć odpowiedź na trzecie z tych pytań. Tutaj następuje kluczowy pomysł: zbudujmy graf pusty
o
wierzchołkach
Dla każdego zadanego przez nas pytania
będziemy w tym grafie łączyć krawędzią wierzchołki
oraz
Zachodzi teraz następujący fakt: jeśli wierzchołki
oraz
należą do tej samej spójnej składowej grafu, to albo zadaliśmy już kiedyś pytanie
albo jesteśmy w stanie wyznaczyć na nie odpowiedź na podstawie dotychczasowych podpowiedzi kuglarza. Istotnie: jeśli wierzchołki te łączy ścieżka
to przez indukcję można pokazać, że możemy wyznaczyć odpowiedzi na kolejne pytania

Rys. 2 Graf po rozważeniu podpowiedzi o kosztach 1 i 2. Zadajemy pytania odpowiadające pogrubionym krawędziom.
Z powyższych rozważań wynikają następujące wnioski. Nie opłaca się prosić o podpowiedzi, które spowodują powstanie cyklu w grafie, zatem po zadaniu dokładnie pytań graf
który nam powstanie, będzie drzewem, a zdobyte informacje umożliwią zlokalizowanie wszystkich kulek (obecność kulki pod
-tym kubkiem to odpowiedź na pytanie
). Zauważmy, że musimy zadać
pytań, bo każda podpowiedź kuglarza daje nam co najwyżej 1 bit informacji, a musimy zgromadzić
bitów, żeby wyznaczyć lokalizację kulek. Ponadto, kolejność zadawania pytań nie ma znaczenia. Pozostaje kwestia, jak wybierać pytania. Rozważając je w kolejności od najmniejszych kosztów i pomijając pytania, na które znamy odpowiedź, możemy znaleźć strategię dla przykładu o koszcie 7 bajtogroszy (Rys. 2).
Zauważmy, że postępując w taki sposób, obliczyliśmy nic innego, jak drzewo rozpinające o minimalnym koszcie dla nieskierowanego grafu pełnego, w którym każda para wierzchołków i
jest połączona krawędzią o wadze
Nasza strategia zachłanna działa dokładnie tak jak algorytm Kruskala, służący do wyznaczania tego drzewa. Ponieważ, jak powiedzieliśmy wyżej, każda strategia zadawania pytań wyznacza nam drzewo rozpinające, zatem, aby znaleźć optymalną strategię, należy znaleźć drzewo rozpinające o minimalnej wadze.

Standardowo robi się to, korzystając właśnie z algorytmu Kruskala lub algorytmu Prima z kolejką priorytetową zrealizowaną za pomocą kopca binarnego. Dla grafu o wierzchołkach i
krawędziach oba algorytmy działają w czasie
W naszym przypadku mamy jednak do czynienia z grafem pełnym, w którym
co daje czas
Można ten czas poprawić, zastępując kolejkę priorytetową w algorytmie Prima zwykłą tablicą, w której czas wyszukiwania będzie
ale czas aktualizacji krawędzi będzie stały. Dzięki temu złożoność rozwiązania zmniejszy się do