Dla dwóch punktów
Początkiem odcinka
W przypadku, gdy wszystkie odcinki leżą na jednej prostej, jedynym rozwiązaniem jest połączyć każde sąsiednie dwa odcinki.
W ogólniejszym przypadku, gdzie wszystkie odcinki są pionowe,
jednym z poprawnych rozwiązań jest połączyć w grupy w powyższy sposób
wszystkie odcinki o tych samych współrzędnych
Zauważmy, że można to zaimplementować po prostu sortując odcinki
po ich początkach według relacji
Przy warunku, że każda para odcinków jest równoległa,
powyższe rozwiązanie nadal działa,
jednak trzeba inaczej posortować odcinki
(tak, aby nadal były według relacji „jest na lewo”).
Parę odcinków
Powyższe podejście sugeruje \textit{zamiatanie} -- rozpatrzenie punktów w określonej kolejności, aby skorzystać z własności relacji i sposobu podzielenia punktów na „już rozpatrzone” oraz „jeszcze nie rozpatrzone”, dzięki czemu można utrzymywać odpowiednią strukturę danych do obliczenia wyniku (potencjalnie prostszą, niż przy innych podejściach).
Rozpatrzmy więc wszystkie punkty
(zarówno początki odcinków, jak i ich końce,
pamiętając przy okazji do których odcinków należą)
w kolejności relacji
Nazwa \textit{zamiatanie} nawiązuje do sposobu wizualizacji
działania algorytmu.
Rozpatrując dany punkt
W tym zadaniu konkretniej mamy do czynienia z odcinkami, więc każdy taki odcinek może mieć stan „aktualny” (przecinający się z miotłą), „rozpatrzony” (na lewo od miotły) oraz „jeszcze nie rozpatrzony” (na prawo od miotły), przy czym ostatni stan oznacza punkt / odcinek jeszcze nie mający wpływu na wypisane rozwiązanie, więc go pomijamy w przykładach.
W momencie, gdy odcinek zmienia swój stan na aktualny, będzie on przypinany nowym odcinkiem do aktualnych/rozpatrzonych odcinków. Kluczem jest znalezienie jakiegoś takiego rozpatrzonego punktu, aby nowo dodany odcinek nie przecinał się z żadnym innym.
Strategia przypinania punktu do najbardziej prawego rozpatrzonego punktu by działała, gdyby tylko nie obecność aktywnych odcinków, przez które nowo powstały odcinek mógłby mieć przecięcia. Dobrym pomysłem jest więc rozpatrywanie punktów tylko pomiędzy aktywnym odcinkiem tuż nad oraz tuż pod rozpatrywanym punktem (gdzie „pomiędzy” jeszcze nie jest formalnie zdefiniowane) i z nich wybranie najbardziej prawego punktu.
We wzorcowym rozwiązaniu utrzymywany jest uporządkowany zbiór
aktywnych odcinków (w kolejności od „dołu” do „góry”).
Można to wykonać korzystając z set z własnym komparatorem
-- kontener utrzymuje indeksy odcinków, a komparator porównuje dwa
odcinki porównując współrzędne
Można dzięki temu wykonać część operacji potrzebnych w rozwiązaniu wzorcowym -- dodanie i usunięcie aktywnego odcinka oraz zapytanie się o pierwszy odcinek „od góry” względem danego punktu na miotle (na przykład korzystając z set.lower_bound). Dla wygody implementacji, żeby taki „pierwszy odcinek” zawsze istniał, można na początku algorytmu dodać sztuczny odcinek o wystarczającej szerokości będący nad wszystkimi odcinkami.
Została więc kwestia szukania punktu najbardziej na prawo wśród punktów między dwoma aktywnymi odcinkami. Aby to zrobić, można zdefiniować obszar między aktywnymi odcinkami jako zbiór wszystkich punktów na prawo od początków tych dwóch odcinków, na lewo od miotły, pod górnym aktywnym odcinkiem oraz (o ile istnieje) nad dolnym aktywnym odcinkiem (tak jak ja rysunku). Jeżeli nie ma żadnego odcinka w tym obszarze, najbardziej prawym punktem może być jeden z początków aktywnych odcinków.
Tablicę rightmost_under[i] dla danego aktywnego odcinka
Algorytm się sprowadza do zamiatania, utrzymywania set-a
oraz tablicy rightmost_under
i łączenia nowego aktywnego odcinka
z rightmost_under[odcinek nad rozpatrywanym].
Działa więc w złożoności
Gdy każdy odcinek jest pionowy lub poziomy, można wykorzystać rozwiązanie wzorcowe z uproszczoną implementacją (chociażby dlatego, że łatwiej jest porównywać odcinki).
Ponieważ limity pozwalają na rozwiązanie kwadratowe, nie trzeba utrzymywać set-a oraz tablicy rightmost_under, tylko wystarczy za każdym nowym aktywnym odcinkiem liniowo przeiterować się po wszystkich rozpatrzonych odcinkach i wyznaczyć odcinki pod oraz nad rozpatrywanym odcinkiem, a następnie wyznaczyć najbardziej prawy punkt iterując się po wszystkich rozpatrzonych punktach i sprawdzając (na przykład iloczynem wektorowym), czy znajduje się w obszarze.