• Nie Znaleziono Wyników

Pochodzenie: Rozwi¸azanie:

Potyczki Algorytmiczne 2005 bil.cpp

Bajtazar i przyjaciele w pi¸atkowy wieczór udali się do klubu na partyjkę bilarda. Jak za-zwyczaj, podczas tego typu spotkań, wywi¸azała się sprzeczka między Bajtazarem, a Bitolem.

Bajtazar zarzucił Bitolowi, że jego strategia gry jest bezsensowna, gdyż kula uderzana przez niego nie ma najmniejszych szans wpaść do łuzy. Bitol natomiast twierdził, że gdyby uderzył kulę dostatecznie mocno, to w końcu wpadłaby ona do jakiejś łuzy. Pomóż rozstrzygn¸ać spór między kolegami.

Napisz program, który stwierdzi, czy faktycznie kula wpadłaby do łuzy, a jeśli tak, to do której.

Zadanie

Napisz program, który:

ˆ wczyta wymiary stołu bilardowego, pocz¸atkow¸a pozycję uderzanej kuli oraz wektor wyznaczaj¸acy ruch kuli po uderzeniu,

ˆ wyznaczy łuzę, do której wpadnie kula lub stwierdzi, że kula nigdy nie wpadnie do żadnej łuzy,

ˆ wypisze wynik.

Wejście

Pierwszy i jedyny wiersz zawiera sześć liczb całkowitych sx, sy, px, py, wx, wy oddzielonych pojedynczymi znakami odstępu, gdzie sx, sy — wymiary stołu bilardowego, 1 ¬ sx, sy ¬ 1 000 000, sx jest parzyste; px, py — współrzędne pocz¸atkowego położenia kuli, 0 ¬ px ¬ sx,0 ¬ py ¬ sy; wx, wy — współrzędne wektora wyznaczaj¸acego ruch kuli, −1 000 ¬ wx, wy ¬ 1 000.

Stół bilardowy ma sx metrów długości i sy metrów szerokości. Łuzy znajduj¸a się w ro-gach stołu oraz na środkach boków o długości sx. Przykładowo, stół o wymiarach (8, 3) ma łuzy w punktach (0, 0), (4, 0), (8, 0), (0, 3), (4, 3), (8, 3). Kule nie wypadaj¸a poza obręb stołu, poruszaj¸a się bez tarcia, a wszystkie odbicia od band podlegaj¸a zasadzie, że k¸at padania rów-na się k¸atowi odbicia. Kula wpada do łuzy, gdy zrów-najdzie się dokładnie w punkcie, w którym znajduje się dana łuza.

Wyjście

Twój program powinien wypisać jeden wiersz zawieraj¸acy nazwę łuzy, do której wpadnie kula, b¸adź słowo NIE, jeśli to się nigdy nie zdarzy. Nazwy kolejnych łuz s¸a następuj¸ace:

ˆ GL — dla łuzy o współrzędnych (0, sy)

ˆ GP — dla łuzy o współrzędnych (sx, sy)

ˆ GS — dla łuzy o współrzędnych (s2x, sy)

ˆ DL — dla łuzy o współrzędnych (0, 0)

ˆ DP — dla łuzy o współrzędnych (sx,0)

ˆ DS — dla łuzy o współrzędnych (s2x,0)

Przykład

Dla następuj¸acego wejścia:

10 5 7 4 1 2

Poprawnym rozwi¸azaniem jest:

DP

GL GS GP

DL DS DP

Ćwiczenia

Proste Średnie Trudne

acm.uva.es - zadanie 10104 spoj.sphere.pl - zadanie 62 acm.uva.es - zadanie 10294 acm.uva.es - zadanie 10179 acm.uva.es - zadanie 10090 acm.sgu.ru - zadanie 292

5.3. Odwrotność modularna

Załóżmy, że mamy dane równanie modularne z jedn¸a niewiadom¸a Literatura [MD] - 3.6 x:

a∗ x ≡ 1 (mod m)

Chcielibyśmy znaleźć liczbę x ∈ {0, 1 . . . m − 1}, dla której powyższe równanie jest spełnione.

Problem ten znany jest jako wyznaczanie odwrotności modularnej.

Nasuwaj¸acym się na samym pocz¸atku pomysłem jest sprawdzenie wszystkich m możliwoś-ci. Takie rozwi¸azanie sprawdza się jednak tylko w przypadku małych wartości liczby m. Należy

zatem zastanowić się nad szybszym rozwi¸azaniem. Kongruencję, któr¸a chcemy rozwi¸azać moż-na przedstawić w innej postaci poprzez wprowadzenie dodatkowej niewiadomej y, która może przyjmować tylko wartości całkowitoliczbowe:

a∗ x + m ∗ y = 1

Z tej postaci od razu widać, że jeśli liczby a i m nie s¸a względnie pierwsze, to równanie nie ma rozwi¸azania. Gdy jednak liczby te s¸a względnie pierwsze, to istnieje nieskończenie wiele rozwi¸azań. Załóżmy, że x0i y0s¸a pewnym rozwi¸azaniem tego równania. Wtedy x1 = x0+m∗k, y1 = y0 − a ∗ k, k ∈ N również s¸a rozwi¸azaniem tego równania, dokonuj¸ac podstawienia, otrzymujemy bowiem:

a∗x1+m∗y1 = a∗(x0+m∗k)+m∗(y0−a∗k) = a∗x0+m∗y0+a∗m∗k−a∗m∗k = a∗x0+m∗y0 = 1 Wyznaczenia wartości x0 oraz y0 można dokonać przy użyciu rozszerzonego algorytmu Euk-lidesa. Nie ma gwarancji, że wyznaczona w ten sposób liczba x0będzie należała do przedziału {0, 1 . . . m − 1} (a takiego właśnie rozwi¸azania poszukujemy). Można to jednak poprawić, wybieraj¸ac zamiast wyznaczonej wartości x0, jej odpowiednik różni¸acy się o wielokrotność liczby m, który, jak pokazaliśmy, również jest rozwi¸azaniem naszego równania.

Listing 5.8 przedstawia implementację funkcji intRevMod(int, int), realizuj¸ac¸a omówiony algorytm. Funkcja ta przyjmuje jako parametry dwie liczby — a oraz m, zwraca natomiast wartość zmiennej x lub −1, jeśli nie istnieje odwrotność liczby a (modulo m).

Listing 5.8: Implementacja funkcji int RevMod(int, int).

// Funkcja wyznacza odwrotność modularną liczby a (mod m) 1 int RevMod(inta, int m){

2 LL x, y;

3 if (GCDW(a, m, x, y) != 1) return -1;

// Dokonaj przesunięcia zmiennej x, tak aby znajdowała się w przedziale [0..m-1]

4 x %= m;

5 if (x < 0) x += m;

6 return x;

7 }

Listing 5.9: Przykładowe wyniki wyliczone przez funkcję int RevMod(int, int) Rownanie: 3*x = 1 (mod 7)

x = 5

Rownanie: 5*x = 1 (mod 11) x = 9

Rownanie: 11*x = 1 (mod 143) Brak rozwiazan

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

01 int main() { 02 inta, m;

// Dla wszystkich par liczb wyznacz rozwiązanie równania modularnego

Listing 5.10: (c.d. listingu z poprzedniej strony) 03 while (cin >> a >> m) {

04 cout << "Rownanie: " << a << "*x = 1 (mod " << m << ")" << endl;

05 intsol = RevMod(a, m);

06 if (sol == -1) cout << "Brak rozwiazan" << endl;

07 else cout << "x = " << sol << endl;

08 }

09 return 0;

10}

5.4. Kongruencje

Dla danej liczby naturalnej n w prosty sposób można wyz- Literatura [MK] - 4.6, 4.7

[TLK] - I.3 naczyć jej reszty z dzielenia przez różne liczby naturalne. Po

wyko-naniu serii takich operacji można zadać sobie pytanie, czy na pod-stawie sekwencji reszt uzyskanych w procesie dzielenia można odt-worzyć wartość liczby n. Pomocne może się tu okazać chińskie

twierdzenie o resztach, zgodnie z którym jeśli mamy dany zbiór liczb parami względnie pier-wszych K = {k1, k2, . . . km}, to dla każdej sekwencji reszt r1, r2, . . . , rmz dzielenia przez liczby ze zbioru K, istnieje dokładnie jedna liczba całkowita w przedziale [0..k1 ∗ k2∗ . . . ∗ km− 1]

daj¸aca takie reszty. Proces odtwarzania wartości n można wykonać krokami, za każdym razem rozwi¸azuj¸ac prosty układ kongruencji postaci:

( x≡ a (mod p) x≡ b (mod q)

i uzyskuj¸ac na skutek jego rozwi¸azania now¸a kongruencję postaci x≡ c (mod r).

Wyjściowy problem sprowadza się zatem do rozwi¸azywania układu dwóch kongruencji.

Zgodnie z chińskim twierdzeniem o resztach, rozpatrywana sekwencja dzielników jest parami względnie pierwsza. Jednak nie jest to warunek wymagany istnienia rozwi¸azania. Okazu-je się, że rozwi¸azanie układu dwóch kongruencji istnieOkazu-je wtedy i tylko wtedy, gdy a ≡ b (mod NW D(p, q)) i można wyrazić je wzorem

x≡ a ∗ β ∗ q + b ∗ α ∗ p (mod N W D(p,q)p∗q )

gdzie α i β to liczby całkowite spełniaj¸ace równanie α ∗ p + β ∗ q = NW D(p, q).

Funkcja bool congr(int, int, int, int, int&, int&) przedstawiona na listingu 5.11 przyjmuje jako parametry liczby a, b, p i q (w takiej właśnie kolejności), a zwraca wartość log-iczn¸a, oznaczaj¸ac¸a istnienie rozwi¸azania układu kongruencji. W przypadku istnienia rozwi¸aza-nia, wartości dwóch ostatnich parametrów funkcji zostaj¸a ustawione na odpowiednio liczby c oraz r spełniaj¸ace kongruencję postaci x ≡ c (mod r).

Listing 5.11: Implementacja funkcji boolcongr(int, int, int, int, int&, int&) // Funkcja wyznacza rozwiązanie układu dwóch kongruencji

01 boolcongr(inta, intb, int p, int q, int &c, int &r) { 02 LL x, y;

Listing 5.11: (c.d. listingu z poprzedniej strony) 03 r = GCDW(p, q, x, y);

// Jeśli liczba a nie przystaje do b (mod nwd(p,q)), to nie ma // rozwiązania

04 if ((a - b) % r) return 0;

// Wyznacz wartości c oraz r zgodnie ze wzorami 05 x = LL(a) + LL(p) * LL(b - a) / r * x;

06 r = LL(p) * LL(q) / r;

07 c = x % r;

08 if (c < 0) c += r;

09 return1;

10}

Listing 5.12: Przykład działanie funkcji bool congr(int, int, int, int, int&, int&) 5 (mod 7), 9 (mod 11), rozwiazanie: 75 (mod 77)

2 (mod 4), 4 (mod 8), rozwiazanie: Brak rozwiazan

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

01 int main() {

02 inta, b, p, q, c, v;

// Dla wszystkich zestawów danych, wyznacz rozwiązania układu kongruencji 03 while(cin >> a >> p >> b >> q) {

04 cout << a << " (mod " << p << "), " << b <<

05 " (mod " << q << "), rozwiazanie: ";

06 if (congr(a, b, p, q, c, v))

07 cout << c << " (mod " << v << ")" << endl;

08 else cout << "Brak rozwiazan" << endl;

09 }

10 return0;

11}