Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2013 mgr
Jerzy Wałaszek
|
Jedną z reguł, którą stworzył przed laty założyciel firmy, John Hardflor, jest "nigdy nie podejmować się wykonania podłogi bez prostych narożników". Firma wciąż stosuje się do tej reguły. Jednakże dalej mają kłopoty w oszacowaniu powierzchni i chcieliby teraz, aby został dla nich opracowany program, który by ich wspierał w tym przedsięwzięciu.
Firmowy kosztorysant chciałby pojawić się na miejscu potencjalnej pracy i zmierzyć wymiary pokoju, kodując je w sposób następujący:
Np. pewien pokój o poniżej przedstawionych wymiarach:
mógłby być zakodowany za pomocą następującego zbioru par: [(N,9) (E,16) (N,4) (E,7) (S,13) (W,23)].
Napisz program, który obliczy powierzchnię. Załóż, że kodowanie jest zawsze prawidłowe - zawsze kończy się w tym samym narożniku, od którego się zaczęło.
THE AREA IS 99999
6 N 9 E 16 N 4 E 7 S 13 W 23
THE AREA IS 235
Niech dana będzie podłoga o następującym kształcie:
Punkt startowy definicji zaznaczyliśmy czerwonym kołkiem. Jeśli założymy następujące kierunki:
To definicja tej podłogi może wyglądać następująco:
18 N 3 E 2 S 2 E 2 N 5 W 5 N 2 W 3 S 5 W 2 N 5 W
2 S 8 E 3 S 2 E 9 N 2 W 4
lub w drugą stronę:
18 E 4 S 2 W 9 N 2 W 3 N 8 E 2 S 5 E 2 N 5 E 3 S
2 E 5 S 5 W 2 N 2 W 2 S 3
W obu przypadkach definiujemy ten sam równoległobok. W wieloboku będą nas interesowały tylko krawędzie pionowe:
Musimy teraz znaleźć współrzędne tych krawędzi. Jeśli umówimy się, że punkt startowy ma współrzędne (0,0), to otrzymamy:
Zwróć uwagę, że współrzędna x obu wierzchołków krawędzi jest taka sama, ponieważ krawędź jest zawsze pionowa. Zatem krawędź definiuje jednoznacznie trójka liczb (x,yp,yk). Przy wyznaczaniu krawędzi musimy znaleźć najmniejszą i największą wartość współrzędnej y. Dla naszego przykładu jest to:
ymax = 8
Zerujemy pole S i rozpoczynamy "przeglądanie" linią poziomą. Wybieramy prostą równoległą do osi OX o współrzędnej y = ymin. W pętli wyznaczamy punkty przecięcia tej prostej z kolejnymi krawędziami naszego równoległoboku. Umawiamy się, że prosta przecina krawędź, jeśli y yp i y < yk. Zapamiętujemy współrzędne x punktów przecięcia, tak aby były posortowane rosnąco:
Wyznaczone współrzędne x tworzą listę. Z listy tej bierzemy pary kolejnych współrzędnych x1 i x2 i dodajemy do S różnicę (x2 - x1). Różnica ta jest równa polu paska o szerokości 1 zawartego w naszym równoległoboku:
Następnie y zwiększamy o 1, co spowoduje podniesienie naszej prostej o 1 w górę. Znów wyznaczamy listę współrzędnych x przecięć tej prostej z krawędziami naszej figury. Z listy bierzemy po dwie współrzędne x i do S dodajemy ich różnicę:
Prosta wędruje o 1 w górę. Zwróć uwagę, że teraz dwie poprzednie krawędzie nie będą przecinały tej prostej, ponieważ ich współrzędne ymax nie są większe od y. Otrzymamy zatem nowe punkty x na liście współrzędnych przecięć:
I dalej:
Zadanie jest zakończone (kolejne zwiększenie y o 1 spowoduje, że prosta nie przetnie się już z żadną z krawędzi naszej figury). Pole figury wynosi S = 86.
Idea podstawowego rozwiązania jest gotowa. jednakże zanim napiszemy gotowy program, musimy rozwiązać kilka podproblemów:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // ACM - Pole powierzchni podłogi // PRG_38 //----------------------------------- #include <iostream> using namespace std; const int MAXINT = 2147483647; struct edge { int x,yp,yk; }; int main() { int n; // liczba wszystkich krawędzi int nv; // liczba krawędzi pionowych int ymin,ymax; // minimalna i maksymalna współrzędna y końców krawędzi int x1,y1,len; // bieżący punkt int y; // pozycja prostej int i,j,k; // indeksy int * VX; // lista współrzędnych x punktów przecięć krawędzi z prostą int S; // pole figury char dir; // kierunek krawędzi edge * VE; // tablica krawędzi pionowych // Odczytujemy liczbę danych do wczytania cin >> n; // Obliczamy liczbę krawędzi pionowych nv = n / 2; // Tworzymy dynamiczną tablicę krawędzi VE = new edge[nv]; // Tworzymy dynamiczną tablicę współrzędnych x VX = new int[nv]; // Inicjujemy punkt początkowy k = x1 = y1 = ymin = ymax = 0; // W pętli odczytujemy dane i tworzymy odpowiednie krawędzie for(i = 0; i < n; i++) { cin >> dir >> len; switch(dir) { case 'E': x1 += len; break; case 'W': x1 -= len; break; case 'N': VE[k].x = x1; VE[k].yp = y1; VE[k++].yk = (y1 += len); break; case 'S': VE[k].x = x1; VE[k].yk = y1; VE[k++].yp = (y1 -= len); break; } if(y1 < ymin) ymin = y1; else if(y1 > ymax) ymax = y1; } // Obliczamy pole figury S = 0; // W pętli wyszukujemy punkty przecięć prostej z kolejnymi krawędziami. // Punkty te składujemy w VX w porządku rosnącym. Znalezione pary // punktów wykorzystujemy do przyrostowego obliczania pola figury. for(y = ymin; y < ymax; y++) { k = 0; // liczba znalezionych punktów przecięć // Przeglądamy kolejne krawędzie for(i = 0; i < nv; i++) if((y >= VE[i].yp) && (y < VE[i].yk)) { for(j = k; j && (VE[i].x < VX[j-1]); j--) VX[j] = VX[j-1]; VX[j] = VE[i].x; k++; } // Dodajemy różnicę par punktów z VX do S for(i = 0; i < k; i += 2) S += VX[i+1] - VX[i]; } // Wyświetlamy wynik cout << "THE AREA IS " << S << endl; delete [] VE; delete [] VX; return 0; } |
Pierwsza modernizacja może polegać na usprawnieniu przesuwania prostej y. Otóż w trakcie odczytu krawędzi zapamiętujemy współrzędne y wierzchołków początkowych w osobnej tablicy VY w taki sposób, aby były posortowane rosnąco i bez powtórzeń. Zapamiętane wartości posłużą do ustawiania prostej y oraz dadzą nam wysokość prostokątów pola.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // ACM - Pole powierzchni podłogi // PRG_39 //----------------------------------- #include <iostream> using namespace std; const int MAXINT = 2147483647; struct edge { int x,yp,yk; }; int main() { int n; // liczba wszystkich krawędzi int nv; // liczba krawędzi pionowych int x1,y1,len; // bieżący punkt int y; // pozycja prostej int i,j,k; // indeksy int * VX; // lista współrzędnych x punktów przecięć krawędzi z prostą int * VY; // lista współrzędnych y, na których ma się zatrzymywać prosta int ny; // liczba elementów w VY int S; // pole figury char dir; // kierunek krawędzi edge * VE; // tablica krawędzi pionowych // Odczytujemy liczbę danych do wczytania cin >> n; // Obliczamy liczbę krawędzi pionowych nv = n / 2; // Tworzymy dynamiczną tablicę krawędzi VE = new edge[nv]; // Tworzymy dynamiczną tablicę współrzędnych x VX = new int[nv]; // Tworzymy dynamiczną tablicę współrzędnych y VY = new int [nv + 1]; // Inicjujemy punkt początkowy ny = k = x1 = y1 = 0; // W pętli odczytujemy dane i tworzymy odpowiednie krawędzie for(i = 0; i < n; i++) { cin >> dir >> len; switch(dir) { case 'E': x1 += len; break; case 'W': x1 -= len; break; case 'N': VE[k].x = x1; VE[k].yp = y1; VE[k++].yk = (y1 += len); break; case 'S': VE[k].x = x1; VE[k].yk = y1; VE[k++].yp = (y1 -= len); break; } // Teraz y1 wstawiamy do VY bez powtórzeń bool not_repeated = true; for(j = 0; j < ny; j++) if(y1 == VY[j]) { not_repeated = false; // y1 już jest w VY break; } if(not_repeated) { for(j = ny; j && (y1 < VY[j-1]); j--) VY[j] = VY[j-1]; VY[j] = y1; ny++; } } // Obliczamy pole figury S = 0; // W pętli wyszukujemy punkty przecięć prostej z kolejnymi krawędziami. // Punkty te składujemy w VX w porządku rosnącym. Znalezione pary // punktów wykorzystujemy do przyrostowego obliczania pola figury. for(y = 0; y < ny - 1; y++) { k = 0; // liczba znalezionych punktów przecięć // Przeglądamy kolejne krawędzie for(i = 0; i < nv; i++) if((VY[y] >= VE[i].yp) && (VY[y] < VE[i].yk)) { for(j = k; j && (VE[i].x < VX[j-1]); j--) VX[j] = VX[j-1]; VX[j] = VE[i].x; k++; } // Dodajemy do S różnicę par z VX pomnożoną przez różnicę // współrzędnych y z VY, co daje pole prostokąta pomiędzy kolejnymi // pozycjami prostej y, która zatrzymuje się na wierzchołkach krawędzi. for(i = 0; i < k; i += 2) S += (VX[i+1] - VX[i]) * (VY[y+1] - VY[y]); } // Wyświetlamy wynik cout << "THE AREA IS " << S << endl; // Usuwamy tablice dynamiczne delete [] VE; delete [] VX; delete [] VY; return 0; } |
Kolejne ulepszenie może polegać na tym, iż krawędzie składujemy w tablicy VE w porządku rosnących współrzędnych yk. W trakcie wyszukiwania punktów przecięć prostej z krawędziami pomijamy, te, których współrzędne yk są mniejsze lub równe bieżącej współrzędnej prostej. Ponieważ będą to zawsze początkowe krawędzie w VE, to możemy zapamiętywać pozycję, od której należy te krawędzie testować – pozycja ta będzie rosła w miarę odrzucania krawędzi, które już nie mogą tworzyć przecięć z prostą. Dzięki temu program nie będzie sprawdzał wszystkich krawędzi za każdym obiegiem pętli. Modyfikację programu pozostawiam zdolniejszym czytelnikom.
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe