Porównywanie ofert
02.06.2009
![]() Rozwiązania nadesłane do tego zadania w wiekszości miały dobre intencje, jednak nie zawsze były poprawnie zaprogramowane. Zdarzyły się też zgłoszenia które, zamiast skorzystać z pomysłu (o którym niżej), opierały się na bardzo skomplikowanych i nieadekwatnych do zadania strukturach z biblioteki standardowej. Nie uzyskały one maksymalnej ilości punktów. Na wejściu dane były dwa zbiory w porządku rosnącym. Należało wypisać ich sumę, iloczyn oraz różnicę symetryczną (tak fachowo nazywają się operacje opisane w zadaniu). Algorytm wzorcowy jest w istocie delikatnie przerobioną procedurą merge ze znanego algorytmu sortującego MergeSort (sortowanie przez scalanie). Ponieważ kilku zawodnikom udało się zaimplementować rozwiązanie wzorcowe, postanowiliśmy ujawnić, dlaczego ono działa, zachowując przy tym pewną dozę precyzji (czyli unikając nadużywania zaimków rzeczownych, przymiotnych i przysłownych). Czytajcie powoli i uważnie. Oznaczmy pierwszy zbiór przez P, a drugi przez Q. Połóżmy palec (wskaźnik) p na ciągu opisującym zbiór P oraz palec q na ciągu opisującym zbiór Q. Oznaczmy wartości, na które wskazują palce przez odpowiednio *p oraz *q, natomiast wartości, które znajdują się w ciągach P oraz Q bezpośrednio przed *p oraz *q jako odpowiednio _p oraz _q. Na razie palce znajdują się na początku ciągów, więc wartości _p oraz _q nie istnieją - nie szkodzi. Jednak o ile któraś z tych wartości istnieje, musi zachodzić _p < *p oraz _q < *q (ponieważ ciągi P oraz Q są uporządkowane rosnąco). Zażądajmy również, aby przez cały czas działania algorytmu zachodziło _q < *p oraz _p < *q - na razie nie jest to problem, gdyż ani _p ani _q nie istnieją. Później będziemy starali się o to, aby zależność ta zachodziła - będzie ona bardzo ważna w analizie algorytmu. Przyjrzyjmy się teraz wartościom *p oraz *q. Może zajść jeden z trzech przypadków:
Powtarzamy powyższe operacje dopóki jeden z palców nie wyjdzie poza granicę swojego ciągu. Załóżmy, że palec p wyszedł poza granice P. W takim wypadku _p ma wartość ostatniej liczby w zbiorze P. Wartość *q jest większa od _p (jest to nasza nieśmiertelna zależność) a wszystkie następne wartości w Q są większe od *q (gdyż Q jest uporządkowany rosnąco). Zatem są też większe od _p, a przez to również od wszystkich innych wartości w P. Widzimy zatem, że wartości pozostałe w ciągu Q występują tylko w nim i dopisujemy je do sumy oraz różnicy symetrycznej zbiorów. Analogicznie rozumujemy jeżeli palec q (a nie p) wyjdzie poza granice swojego ciągu. Przedstawione rozumowanie wykorzystuje niezmiennik - niezwykle ważne narzędzie w analizie algorytmów. Rozpoczynając analizę zauważyliśmy pewną własność (_p < *q, _q < *p), która w danej chwili była prawdziwa. Konstruując algorytm zadbaliśmy o to, aby w każdym jego kroku własność ta pozostawała prawdziwa, dzięki czemu mogliśmy (właściwie to algorytm mógł) z niej korzystać przy podejmowaniu decyzji. Przyjmując (podobnie jak w zadaniu) za
Wiktor Janas |
Copyright © 2008-2010 Wrocławski Portal Informatyczny
design: rafalpolito.com