Ucieczka
Wyobraź sobie, Drogi Czytelniku, że jesteś kapitanem okrętu wojennego i w trakcie jednej z misji znalazłeś się na środku morza leżącego na terytorium wroga. Wiesz, że wróg rozmieścił w tej strefie pewną (skończoną) liczbę radarów. Każdy radar ma określony zasięg, być może różny w przypadku różnych radarów, i jest w stanie wykryć każdy podejrzany obiekt, który znajdzie się w jego zasięgu. Naszym siłom wywiadowczym udało się wykraść plan rozmieszczenia radarów. Na jego podstawie chcesz stwierdzić, czy możesz wydostać się z wrogich wód niezauważony przez radary.
Powyższa historia wojenna z lotu ptaka wygląda następująco: na płaszczyźnie zadana jest pewna liczba kół stanowiących obszary zabronione. Dla uproszczenia nasz statek również przedstawimy jako koło. Naszym zadaniem jest sprawdzić, czy możemy przemieścić się statkiem nieskończenie daleko od początkowej pozycji, nie dotykając przy tym żadnego z pozostałych kół (Rys. 1).
Takie sformułowanie problemu nie jest jednak zbyt wygodne. Możemy je uprościć przez „odpompowanie” koła reprezentującego statek i „napompowanie” kół przedstawiających zasięgi radarów. Dokładniej, promienie wszystkich kół-radarów zwiększamy o promień statku, a sam statek zmniejszamy do jednego punktu – środka koła (Rys. 2). Aby uzasadnić poprawność tego przekształcenia, wystarczy zauważyć, że bezpieczna trasa statku charakteryzuje się tym, iż jego środek nie zbliża się do żadnego radaru na odległość mniejszą niż suma promienia statku i zasięgu radaru. Po tej transformacji dużo łatwiej udzielić odpowiedzi na pytanie postawione w zadaniu; w sytuacji z rysunku 2 ucieczka statku ewidentnie nie jest możliwa, ale gdyby np. nie było prawego górnego radaru, to można byłoby wskazać bezpieczną trasę ucieczki.
A może dałoby się w ogóle pozbyć z problemu wszystkich kółek? Okazuje się, że jest to możliwe. Intuicja jest taka, że pojedynczy radar bardzo łatwo ominąć, ale za pomocą radarów, których zasięgi nachodzą na siebie, można zbudować już bardzo skuteczną pułapkę. Przedstawmy więc całą sytuację jako graf którego wierzchołki reprezentują lokalizacje radarów, a dwa wierzchołki są połączone krawędzią, gdy zasięgi odpowiadających im radarów przecinają się (Rys. 3). Nasz statek może opuścić terytorium wroga wtedy i tylko wtedy, gdy nie jest otoczony żadnym cyklem grafu
Uzasadnienie tego faktu w jedną stronę jest oczywiste: jeśli statek jest otoczony cyklem, to na pewno nigdy się z niego nie wydostanie. W drugą stronę trzeba się trochę bardziej nagimnastykować, bo nie każda trasa ucieczki „z grafu” faktycznie unika wszystkich radarów. Przykładowo, na rysunku 4 (przedstawiającym graf odpowiadający sytuacji bez prawego górnego radaru) trasa narysowana linią przerywaną nie przecina krawędzi grafu, ale nie jest bezpieczną trasą ucieczki. Poprawne uzasadnienie może wyglądać, na przykład, tak: dzielimy graf na spójne składowe i dla zbioru okręgów z każdej składowej wyznaczamy ich zewnętrzny obrys. Skoro statek nie jest otoczony żadnym cyklem grafu, to możemy nim dopłynąć do obrysu najbliższej składowej, a następnie przepłynąć do jakiegokolwiek punktu wzdłuż obrysu. Z któregoś takiego punktu będziemy mogli albo już bezpośrednio uciec w siną dal, albo przedostać się do obrysu innej składowej itd.
Pozostał nam już tylko problem stwierdzenia, czy statek znajduje się wewnątrz jakiegoś cyklu, czy też nie. W tym momencie ktoś mógłby przypomnieć sobie klasyczny algorytm sprawdzania, czy zadany punkt leży we wnętrzu danego wielokąta (niekoniecznie wypukłego). Aby to sprawdzić, wypuszczamy z tego punktu półprostą w losowym kierunku i zliczamy jej przecięcia z brzegiem wielokąta; jeśli jest ich nieparzyście wiele, to punkt leży wewnątrz wielokąta, a jeśli parzyście wiele, to na zewnątrz. Losowość kierunku gwarantuje, że półprosta nie przejdzie przez żaden wierzchołek wielokąta, dzięki czemu unikamy rozważania przykrych przypadków szczególnych. Niestety, w naszym problemie możemy mieć w grafie coś więcej niż jeden wielokąt, co powoduje, że zależnie od kierunku półprostej liczba przecięć może być parzysta bądź nieparzysta (Rys. 5). Klasyczny algorytm tutaj nie zadziała.
Warto jednak pozostać przy pomyśle z półprostą, tylko sprytniej go wykorzystać. Chcemy stwierdzić, czy w grafie istnieje taki cykl, że łączna liczba przecięć krawędzi tego cyklu z wybraną półprostą jest nieparzysta. Krawędzie grafu przecinające półprostą zmieniają parzystość licznika przecięć, oznaczymy je zatem jedynką, a pozostałe krawędzie poetykietujemy zerami.
Teraz przyszedł czas na kluczowy pomysł: stworzymy nowy graf zawierający dwie kopie oryginalnego grafu. Dokładniej, dla każdego wierzchołka grafu do grafu dodamy wierzchołki i tutaj druga współrzędna oznacza parzystość liczby przecięć z wybraną półprostą. Jeśli w grafie mamy krawędź łączącą wierzchołki i o etykiecie 0, to w grafie tworzymy dwie krawędzie, łączące z oraz z A jeśli rozważana krawędź grafu ma etykietę 1, to w grafie łączymy wierzchołki z oraz z – patrz rysunek 6.
Był już kluczowy pomysł, teraz pora na kluczowe spostrzeżenie: cykl złożony z krawędzi grafu uniemożliwiający statkowi ucieczkę w grafie odpowiada po prostu ścieżce z jakiegoś wierzchołka do Wystarczy zatem stwierdzić, czy w grafie jakaś para wierzchołków postaci należy do tej samej spójnej składowej. Nie trzeba wielkiego zacięcia algorytmicznego, by uwierzyć, że sprawdzenie tak sformułowanego warunku nie może już być trudne. Faktycznie, do podziału grafu na spójne składowe można zastosować praktycznie dowolny algorytm przeszukiwania grafu, a najwygodniej – przeszukiwanie w głąb. Ostateczne kryterium stwierdzające możliwość ucieczki statku okazało się zatem niezbyt skomplikowane i, co ciekawe, niespecjalnie związane z geometrią.