• Nie Znaleziono Wyników

Pochodzenie: Rozwi¸azanie:

VI Olimpiada Informatyczna altar.cpp

Według chińskich wierzeń ludowych złe duchy mog¸a poruszać się tylko po linii prostej. Ma to istotne znaczenie przy budowie świ¸atyń. Świ¸atynie s¸a budowane na planach prostok¸atów o bokach równoległych do kierunków północ-południe oraz wschód-zachód. Żadne dwa z tych prostok¸atów nie maj¸a punktów wspólnych. Po środku jednej z czterech ścian jest wejście, którego szerokość jest równa połowie długości tej ściany. W centrum świ¸atyni (na przecię-ciu przek¸atnych prostok¸ata) znajduje się ołtarz. Jeśli znajdzie się tam zły duch, świ¸atynia zostanie zhańbiona. Tak może się zdarzyć, jeśli istnieje półprosta (w płaszczyźnie równoległej do powierzchni terenu), która biegnie od ołtarza w centrum świ¸atyni przez otwór wejściowy aż do nieskończoności, nie przecinaj¸ac i nie dotykaj¸ac po drodze żadnej ściany, tej lub innej świ¸atyni.

Zadanie

Napisz program, który:

ˆ wczyta opisy świ¸atyń,

ˆ sprawdzi, które świ¸atynie mog¸a zostać zhańbione,

ˆ wypisze ich numery na standardowe wyjście

Wejście

W pierwszym wierszu wejścia zapisana jest jedna liczba naturalna n, 1 ¬ n ¬ 1 000, będ¸aca liczb¸a świ¸atyń. W każdym z kolejnych n wierszy znajduje się opis jednej świ¸atyni (w i-tym z tych wierszy opis świ¸atyni numer i). Opis świ¸atyni składa się z czterech nieujemnych liczb całkowitych, nie większych niż 8 000 oraz jednej litery E, W, S lub N. Pierwsze dwie liczby, to współrzędne północno-zachodniego narożnika świ¸atyni, a dwie następne, to współrzędne przeciwległego, południowo-wschodniego narożnika. Określaj¸ac współrzędne punktu poda-jemy najpierw jego długość geograficzn¸a (która rośnie z zachodu na wschód), a następnie

— szerokość geograficzn¸a, która rośnie z południa na północ. Pi¸aty element opisu wskazuje ścianę, na której znajduje się wejście do świ¸atyni (E — wschodni¸a, W — zachodni¸a, S — południow¸a, N — północn¸a). Kolejne elementy opisu świ¸atyni s¸a pooddzielane pojedynczymi odstępami.

Wyjście

W kolejnych wierszach wyjścia, Twój program powinien zapisać w porz¸adku rosn¸acym nu-mery świ¸atyń, które mog¸a zostać zhańbione przez złego ducha, każdy numer w osobnym wierszu.

Przykład

Dla następuj¸acego wejścia:

6

1 7 4 1 E 3 9 11 8 S 6 7 10 4 N 8 3 10 1 N 11 4 13 1 E 14 8 20 7 W

Poprawnym rozwi¸azaniem jest:

1 2 5 6

Ćwiczenia

Proste Średnie Trudne

acm.uva.es - zadanie 10002 acm.uva.es - zadanie 10927 spoj.sphere.pl - zadanie 202

X

Rysunek 3.10: (a) Zbiór ośmiu punktów na płaszczyźnie oraz najmniejsza wypukła otoczka dla tego zbioru punków. (b) Dla każdej pary punktów oddalonych o d, z których co najmniej jeden nie leży na wypukłej otoczce, istnieje para punktów oddalona o l > d.

3.7. Wypukła otoczka

Dla danego zbioru punktów S, wypukła otoczka jest to wielok¸at Literatura [WDA] - 35.3

[ASD] - 8.3 [RII] - 4 wypukły, który zawiera wszystkie punkty ze zbioru S. Najmniejsza

wypukła otoczka cechuje się dodatkowo najmniejszym możliwym polem. Ważn¸a własności¸a najmniejszej wypukłej otoczki poma-gaj¸ac¸a w jej wyznaczaniu jest to, że każdy jej wierzchołek to również punkt ze zbioru S (patrz rysunek 3.10.a)

Istnieje wiele algorytmów służ¸acych do wyznaczania najmniejszej wypukłej otoczki. Jed-nym z nich jest algorytm Grahama, który dla n-elementowego zbioru punktów działa w czasie O(n ∗ log(n)). Algorytm ten najpierw wyznacza punkt zbioru o najmniejszej odciętej (współrzędnej x), a następnie sortuje wszystkie inne w kolejności rosn¸acych k¸atów odchyle-nia (patrz rysunek 3.11.a). Wszystkie punkty s¸a następnie przetwarzane w takiej kolejności

— dodaje się je do konstruowanej wypukłej otoczki, usuwaj¸ac jednocześnie punkty dodane poprzednio, o ile k¸at utworzony pomiędzy trzema ostatnio dodanymi do otoczki punktami wyznacza skręt w prawo (rysunek 3.11.d). Proces konstrukcji wypukłej otoczki dla przykład-owego zbioru punktów został przedstawiony na rysunku 3.11.

Przedstawiona na listingu 3.43 funkcjavector<POINT*> ConvexHull(vector<POINT>&) przyjmuje jako parametr wektor punktów, a zwraca wektor wskaźników na punkty należ¸ace do wypukłej otoczki. Wskaźniki te s¸a uporz¸adkowane w kolejności odwrotnej do ruchu wskazówek zegara. Przedstawiona funkcja jest modyfikacj¸a opisanego algorytmu Grahama, maj¸ac¸a na celu skrócenie implementacji oraz zwiększenie wydajności algorytmu. Punkty nie s¸a sor-towane po k¸acie odchylenia, lecz w porz¸adku „po współrzędnych”, natomiast konstrukcja otoczki realizowana jest w dwóch fazach — dolnej oraz górnej połówki.

Listing 3.43: Implementacja funkcji vector<POINT*> ConvexHull(vector<POINT>&) 01#define XCAL {while(SIZE(w) > m && Det((*w[SIZE(w) - 2]), (*w[SIZE(w) - 1]), \ 02 (*s[x])) <= 0) w.pop back(); w.PB(s[x]);}

// Funkcja wyznaczająca wypukłą otoczkę dla zbioru punktów 03vector<POINT*> ConvexHull(vector<POINT>& p) {

04 vector<POINT*> s, w;

// Wypełnij wektor s wskaźnikami do punktów, // dla których konstruowana jest wypukła otoczka 05 FOREACH(it, p) s.PB(&*it);

Listing 3.43: (c.d. listingu z poprzedniej strony) // Posortuj wskaźniki punktów w kolejności

// (niemalejące współrzędne x, niemalejące współrzędne y) 06 sort(ALL(s), OrdXY);

07 int m = 1;

// Wyznacz dolną część wypukłej otoczki - łączącą najbardziej lewy - dolny // punkt z najbardziej prawym - górnym punktem

08 REP(x, SIZE(s)) XCAL 09 m = SIZE(w);

// Wyznacz górną część otoczki 10 FORD(x, SIZE(s) - 2, 0) XCAL

// Usuń ostatni punkt (został on wstawiony do otoczki dwa razy) 11 w.pop back();

12 return w;

13}

Wiele problemów dla zbioru punktów można rozwi¸azać, wyznaczaj¸ac najpierw wypukł¸a oto-czkę. Przykładem może być problem znajdowania pary najbardziej oddalonych punktów.

Oczywistym jest, że punkty takie musz¸a znajdować się na wypukłej otoczce rozpatrywanego zbioru. Wybieraj¸ac bowiem parę punktów A i B oddalonych od siebie o d, z których przyna-jmniej jeden nie leży na wypukłej otoczce (załóżmy bez straty ogólności, że jest nim punkt A) oraz prowadz¸ac przez punkt A prost¸a l prostopadł¸a do odcinka A 7→ B, okazuje się, że dla wszystkich punktów C, leż¸acych po przeciwnej stronie prostej l niż punkt B, zachodzi

|B 7→ C| > d (patrz rysunek 3.10.b). W celu wyznaczenia najdalszej pary punktów, należy dla każdego punktu leż¸acego na wypukłej otoczce wyznaczyć punkt dla niego najdalszy oraz wybrać spośród wszystkich znalezionych par t¸a najbardziej oddalon¸a. Można tego dokon-ać w czasie liniowym poprzez wyznaczanie kolejnych par antypodycznych punktów, co daje sumaryczn¸a złożoność czasow¸a algorytmu O(n ∗ log(n)).

Listing 3.44: Przykład działania funkcji vector<POINT*> ConvexHull(vector<POINT>&)dla zbioru punktów z rysunku 3.10. Do wyznaczanej wypukłej otoczki należ¸a tylko skrajne punkty krawędzi — punkt (7, 4) nie należy do wyznaczonej otoczki.

(-7, 5) (-6, -2) (5, -2) (8, 7)

Listing 3.45: Kod źródłowy programu użytego do wyznaczenia wyniku z listingu 3.44. Pełny kod źródłowy programu znajduje się w pliku convexhull str.cpp

01 intmain() { 02 int n;

03 vector<POINT> l;

04 POINT p;

// Wczytaj liczbę punktów

05 cin >> n;

// Wczytaj wszystkie punkty 06 REP(x, n) {

07 cin >> p.x >> p.y;

08 l.PB(p);

09 }

Listing 3.45: (c.d. listingu z poprzedniej strony) // Wyznacz wypukłą otoczkę

10 vector<POINT *> res = ConvexHull(l);

11 FOREACH(it, res)

12 cout << " " << (**it);

13 return0;

14}