Informatyczny kącik olimpijski
Zliczanie prostokątów
W tym kąciku rozwiążemy zadanie Prostokąty (Rectangles), które pojawiło się w 2007 roku na konkursie organizowanym przez MIT w serwisie spoj.pl.
Zadanie. Na płaszczyźnie danych jest różnych punktów. Na ile sposobów można wybrać cztery z nich tak, aby były one wierzchołkami prostokąta o bokach równoległych do osi układu współrzędnych?
Pierwsze rozwiązanie, które powinno przyjść nam do głowy, wykorzystuje obserwację, że każdy szukany prostokąt jest jednoznacznie określony przez jego dwa przeciwległe wierzchołki. Możemy więc dla każdej pary danych punktów (lewy dolny, prawy górny) sprawdzić, czy do zbioru należą dwa pozostałe wierzchołki. Jeśli do sprawdzania przynależności do zbioru wykorzystamy tablicę haszującą, takie rozwiązanie będzie działało w czasie
Może się wydawać, że sporo czasu w powyższym rozwiązaniu tracimy na sprawdzaniu „złych” par – przekątnych, dla których pozostałe dwa punkty nie istnieją. Niestety, prosty przykład punktów ustawionych w „dwuszereg” pokazuje, że wynik może być rzędu zatem żaden algorytm zliczający prostokąty pojedynczo nie może być istotnie lepszy od powyższego.
Jak zatem znaleźć lepsze rozwiązanie? Zauważmy na początek, że możemy przenumerować współrzędne na wejściu tak, aby wszystkie były naturalne i nie większe niż Nie interesują nas bowiem długości boków czy pola prostokątów, tylko ich liczba. Podzielmy punkty na pionowe bloki, tj. ze względu na współrzędną Bloki natomiast podzielmy na dwie kategorie – małe, czyli te, które zawierają co najwyżej punktów, i duże, czyli pozostałe.
Zapomnijmy na moment o dużych blokach. Powiemy, że współrzędna należy do bloku, jeśli w bloku znajduje się punkt o drugiej współrzędnej równej Będziemy iterować po możliwych położeniach dolnego boku prostokąta, od góry do dołu. Załóżmy, że dolny bok prostokąta ma drugą współrzędną równą i rozważmy te bloki, do których należy punkt o takiej współrzędnej – punkty w pozostałych blokach nie mają szans należeć do takiego prostokąta. Ignorujemy także wszystkie punkty o drugich współrzędnych nie większych niż – te także nie mają wpływu na wynik. Teraz wystarczy policzyć, ile jest poziomych odcinków o obu końcach wśród punktów, które nam pozostały – jako że każdy blok zawiera każdy taki odcinek odpowiada jednemu prostokątowi. Ten krok można wykonać łatwo, przechodząc po wszystkich punktach, które nam pozostały (w dowolnej kolejności, np. od lewej do prawej), przy okazji zliczając w tablicy (współrzędne są małe!), ile razy dana rzędna już wystąpiła, i aktualizując wynik.
Załóżmy, że wielkości małych bloków to przy czym i Przy uważnej implementacji algorytmu małych bloków wykonamy w nim liczbę operacji co najwyżej rzędu Faktycznie, wybrana współrzędna będzie należała do -tego bloku razy, i wówczas w fazie przeglądania każdy punkt tego bloku odwiedzimy co najwyżej raz. Wykażemy, że Spróbujmy znaleźć „najgorszy możliwy” ciąg Jeśli dla pewnych różnych mamy to zmniejszając o i zwiększając o nie zmieniamy a jednocześnie zwiększamy Jest to konsekwencja wypukłości funkcji kwadratowej. Powtarzając tę operację dostatecznie dużo razy, otrzymamy wszystkie liczby – być może z wyjątkiem jednej – równe lub Tych niezerowych będzie co najwyżej ponieważ Ostatecznie, dostajemy
Pozostało nam zająć się dużymi blokami. Dla dowolnego bloku łatwo policzyć w czasie ile jest prostokątów, które mają dwa punkty w tym bloku – jeśli dla każdego pozostałego bloku znamy liczbę współrzędnych wspólnych dla i to szukaną przez nas liczbą jest Ponieważ tę sumę dodamy do wyniku dla każdego dużego bloku, więc prostokąty o wszystkich punktach w dużych blokach policzymy dwukrotnie To jednak nie jest duży problem – aby się z nim uporać, wystarczy dla każdego dużego bloku zawrzeć w sumie wszystkie małe bloki i tylko te duże, które znajdują się na lewo od niego. Na szczęście, duże bloki są wystarczająco liczne, aby nie mogło ich być więcej niż – a więc ta faza działa także w czasie W powyższych rozważaniach zapomnieliśmy o kosztach związanych z sortowaniem i przenumerowaniem wejściowych współrzędnych. Te nie powinny wynieść jednak więcej niż operacji, więc są znikome w porównaniu z kosztem faz zliczających. Całe rozwiązanie działa więc w czasie i pamięci
Zainteresowanemu Czytelnikowi polecamy zastanowienie się, co by było, gdybyśmy chcieli, zamiast prostokątów, zliczać kwadraty. Czy umiemy to zrobić szybciej, czy potrzebujemy bardziej skomplikowanych struktur danych? Tak sformułowane zadanie pojawiło się na Obozie Naukowo-Treningowym im. A. Kreczmara w 2010 roku pod tytułem Ogródek i można je rozwiązywać w serwisie main.edu.pl.