Rozwiązania można oglądać we wtorek 29 marca, między 15:00 a 16:30 w 5790. Można też próbować w innych terminach, ale nie ma gwarancji, że będę u siebie w pokoju.

Część A

Wszystkie poprawne rozwiązania sprowadzały się w zasadzie do tego samego pomysłu. Należało skonstruować graf o O(n) wierzchołkach i O(n2) krawędziach i użyć algorytmu MKM do znalezienia w nim maksymalnego przepływu. Całość działała wówczas w czasie O(n3). Na rozwiązanie powinny składać się następujące części (poniżej zakładam, że suma ci jest równa sumie ri): Kryteria oceny:

Część B

Kilka słów o redakcji rozwiązań

Rozwiązania różniły się od siebie znacznie pod względem długości. Najkrótsze w pełni poprawne mieściło się na jednej stronie, zaś najdłuższe zajmowało 7 stron. Zazwyczaj im dłuższe było rozwiązanie, tym większe fragmenty dawało się usunąć bez straty dla jego jakości. Poniżej zamieszczam przykładowy opis rozwiązania części B z kilkoma uwagami i poradami. Zwracam uwagę na rzeczowy i zwięzły sposób opisania rozwiązania - lepiej zredagowane rozwiązania pozwolą nam je szybciej oceniać.

Mamy graf o wierzchołkach s, t, R1, R2, ..., Rn, C1, C2, ..., Cn. Od s do Ri prowadzi krawędź o przepustowości ri, między Ri a Cj są krawędzie jednostkowe, od Cj do t prowadzą krawędzie o przepustowościach cj. Na podstawie rozważań z części A wiemy, że wystarczy znaleźć w tym grafie wartość maksymalnego przepływu, czyli, z twierdzenia o MP i MP, wartość minimalnego przekroju.

Niech (S, V - S) to dowolny przekrój w tym grafie. Do części S musi należeć źródło s. Oznaczamy przez SR zbiór tych elementów z części R, które nie należą do S zaś przez SC - przecięcie S z częścią C. Jeśli |SR| = k, zaś |SC| = l, to przepustowość tego przekroju to
ri1 + ri2 + ... + rik + cj1 + cj2 + ... + cjl + (n-k)(n-l)
dla pewnych ciągów indeksów i oraz j.

Jasne jest, że dla ustalonych k i l powyższa suma jest najmniejsza, jeśli weźmiemy odpowiednio k oraz l najmniejszych wyrazów z ciągów r oraz c. (Tu chciałbym zwrócić uwagę, że nieudowadnianie tego faktu jest wskazane, bo poprawia czytelność rozwiązania). Na początku algorytmu możemy sprawdzić, czy wszystkie wyrazy tych ciągów są mniejsze niż n i, jeśli tak jest, sortujemy ciągi przez zliczanie, w O(n). Jeśli pewien wyraz jest większy od n, rozwiązanie nie istnieje. Dalej zakładamy więc, że ciągi te są posortowane niemalejąco.

Możemy teraz zapisać przepustowość minimalnego przekroju (o odpowiednich rozmiarach przecięć z poszczególnymi częściami grafu) w następującej postaci:
r1 + r2 + ... + rk + (c1 + k-n) (c2 + k-n) ... + (cl + k-n) + (n-k)n
Znajdziemy minimum powyższego wyrażenia dla każdego k. Zauważmy, że najlepiej wziąć takie l, by zsumować wszystkie wyrazy cj ≤ n-k. Korzystamy tu jedynie z faktu, że podzbiór pewnego zbioru liczb całkowitych A ma najmniejszą sumę, gdy składa się ze wszystkich liczb nieujemnych w A.
Obliczamy zatem (w O(n)) tablicę indeksowaną liczbami od 0 do n, która w komórce i pamięta ile jest wyrazów ciągu c nie większych od i oraz jaka jest ich suma. To pozwala znaleźć minimalną wartość badanego wyrażenia dla każdego k w czasie stałym. Tym sposobem możemy w czasie liniowym znaleźć przepustowość minimalnego przekroju.

Warto jeszcze dodać, że opis w postaci podobnej do powyższej jest dużo łatwiejszy do zrozumienia niż napisanie kodu rozwiązania i tłumaczenie poszczególnych wierszy. Pseudokod przydaje się, gdy chcemy pokazać jak wygląda przepływ sterowania między poszczególnymi fazami algorytmu (np. w opisie algorytmu Dinica), jednak jeśli algorytm daje się poskładać z kilku prostych kawałków, zwyczajny opis wydaje się lepszym rozwiązaniem.