Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Oszczędny kod

Tomasz Kulczyński

o artykule ...

  • Publikacja w Delcie: luty 2011
  • Publikacja elektroniczna: 01-02-2011

Tematem kolejnej edycji Kącika jest zadanie pochodzące z zeszłorocznej Międzynarodowej Olimpiady Informatycznej o nazwie Oszczędny kod (ang. Saveit).

Problem. Naszym celem jest zakodowanie, a później odkodowanie pewnych informacji. Musimy zaimplementować dwie części programu, z których pierwsza otrzyma pewien nieskierowany spójny graf oraz liczbę  math . Jej zadaniem jest wygenerowanie ciągu bitów. Na podstawie tegoż ciągu, liczby  math  liczby wierzchołków grafu – oraz liczby  math , druga część programu powinna odkodować interesujące nas informacje, tzn. najkrótsze odległości pomiędzy pewnymi parami wierzchołków grafu. Wyjaśnijmy teraz, że wierzchołki grafu są ponumerowane od math do  math , a interesują nas odległości wierzchołków o numerach od math do  math do wszystkich wierzchołków grafu. Kluczowe w zadaniu jest wygenerowanie możliwie krótkiego ciągu bitów, który pozwoli na odkodowanie potrzebnych informacji. Przyjmujemy, że numery wierzchołków (oraz inne liczby mniejsze niż  math ) możemy zapisać, używając math bitów (czyli w istocie math).

Najprostszym sposobem jest po prostu przesłanie całego grafu. Niewątpliwie jest to wystarczające do uzyskania wszelkich informacji na jego temat. Graf można przesłać jako liczbę krawędzi, a następnie ich listę. W przypadku, gdy graf jest pełny, daje to math bitów. Można też przesłać macierz sąsiedztwa, która, naturalnie, zawiera math istotnych bitów (mimo że jest ich łącznie math , ale połowę z nich można ustalić, korzystając z symetryczności tej macierzy).

Kolejnym pomysłem jest najpierw obliczyć wszystkie interesujące nas odległości, a następnie zakodować je i przesłać do drugiej części programu. Interesujących nas liczb jest  math i każda zajmie math bitów, co da łącznie math bitów. Jest to dosyć satysfakcjonujące rozwiązanie i było na Olimpiadzie doceniane połową liczby punktów za to zadanie.

Istnieją jednak lepsze rozwiązania, opierające się na następujących spostrzeżeniach. Przede wszystkim zwróćmy uwagę na to, jaki jest związek odległości dwóch sąsiednich wierzchołków math i  math od ustalonego wierzchołka  math. Otóż, nie mogą one różnić się o więcej niż 1. Weźmy bowiem najkrótsze ścieżki z  math do tych dwóch wierzchołków, długości mathmath, odpowiednio. Oczywiście, moglibyśmy z  math pójść do  math, a następnie przejść krawędzią z  math do  math, co dałoby odległość math, a skoro długość najkrótszej ścieżki z  math do  math to math, więc mamy math. Analogicznie w drugą stronę. Różnica między mathmath należy więc do zbioru math.

Aby skorzystać z powyższego faktu, musimy najpierw wiedzieć o jakichś parach wierzchołków, że są sąsiadami. W związku z tym spójrzmy na wierzchołek o numerze  math i dla każdego innego wierzchołka  math znajdźmy  math – najbliższy od  math wierzchołek na najkrótszej ścieżce z  math do  math. Zauważmy, że pary math to krawędzie naszego grafu, które tworzą drzewo rozpinające. Na ich podstawie możemy łatwo wyznaczyć odległości wszystkich wierzchołków od  math ze wzoru math. Zastanówmy się teraz, jak wyznaczyć odległości od wszystkich wierzchołków z wierzchołka math . Przede wszystkim znamy math. Ponadto, dla każdego math mamy: math, przy czym wiemy już, że math, gdyż math i  math są sąsiadami. W takim razie wystarczy przesłać wszystkie liczby math oraz math, które zajmują math bitów. Dwójka oznacza tu dwa bity potrzebne na przesłanie liczby ze zbioru  math.

Ostatnie usprawnienie, wymagane na zawodach do uzyskania maksymalnej noty, polega na tym, aby zmniejszyć tę właśnie dwójkę do jakiejś mniejszej stałej. Widzimy bowiem, że nie wykorzystujemy w pełni tych dwóch bitów, skoro kodujemy na nich tylko trzy różne możliwości, zamiast dostępnych czterech. Dobrym pomysłem jest spojrzenie na pełen bajt (tzn. 8 bitów) i spostrzeżenie, że można w nim zakodować math różnych możliwości, a więc w szczególności math różnych możliwości, czyli pięć wartości math. Stąd dwójkę możemy mniej więcej zastąpić liczbą  math – liczba wykorzystanych bitów to wówczas math .

Zachęcamy Czytelnika do zastanowienia się, czy do rozwiązania zadania wystarczy użyć math bitów (zauważmy, że nie jest to wielka poprawka, gdyż math). Warto także zastanowić się, jak szybko działają wszystkie opisane wyżej rozwiązania.