• Nie Znaleziono Wyników

Pochodzenie: Rozwi¸azanie:

VIII Olimpiada Informatyczna comm.cpp

W parlamencie Demokratycznej Republiki Bajtocji, zgodnie z Bardzo Ważn¸a Ustaw¸a, należy ukonstytuować Komisję Poselsk¸a do Spraw Spokoju Publicznego. Niestety sprawę utrudnia fakt, iż niektórzy posłowie wzajemnie się nie lubi¸a.

Komisja musi spełniać następuj¸ace warunki:

ˆ każda partia ma dokładnie jednego reprezentanta w Komisji,

ˆ jeśli dwaj posłowie się nie lubi¸a, to nie mog¸a jednocześnie być w Komisji.

Każda partia ma w parlamencie dokładnie dwóch posłów. Wszyscy posłowie s¸a ponumerowani liczbami od 1 do 2n. Posłowie o numerach 2i − 1 i 2i należ¸a do partii o numerze i.

Zadanie

Napisz program, który:

ˆ wczyta liczbę partii oraz pary posłów, którzy się wzajemnie nie lubi¸a,

ˆ wyznaczy skład Komisji, lub stwierdzi, że nie da się jej ukonstytuować,

ˆ wypisze wynik

Wejście

W pierwszym wierszu wejścia znajduj¸a się dwie nieujemne liczby całkowite n i m. Liczba n, spełniaj¸aca warunki 1 ¬ n ¬ 8 000, oznacza liczbę partii. Liczba m, spełniaj¸aca warunki 0 ¬ m ¬ 20 000, oznacza liczbę par nielubi¸acych się posłów. W każdym z kolejnych m wier-szy zapisana jest para liczb naturalnych a i b, 1 ¬ a 6= b ¬ 2n, oddzielonych pojedynczym odstępem. Oznacza ona, że posłowie o numerach a i b wzajemnie się nie lubi¸a.

Wyjście

W pierwszym i jedynym wierszu wyjścia powinno znaleźć się pojedyncze słowo NIE, jeśli utworzenie Komisji nie jest możliwe. W przypadku, gdy utworzenie Komisji jest możliwe, pro-gram powinien wypisać n liczb całkowitych z przedziału od 1 do 2n, zapisanych w kolejności rosn¸acej i oznaczaj¸acych numery posłów zasiadaj¸acych w Komisji. Każda z tych liczb powin-na zostać zapisapowin-na w osobnym wierszu. Jeśli Komisję możpowin-na utworzyć powin-na wiele sposobów, Twój program może wypisać dowolny z nich.

Przykład

Dla następuj¸acego wejścia:

3 2 1 3 2 4

Poprawnym rozwi¸azaniem jest:

1 4 6

Ćwiczenia

Proste Średnie Trudne

acm.uva.es - zadanie 124 acm.uva.es - zadanie 200 acm.uva.es - zadanie 10319 acm.uva.es - zadanie 10305 acm.sgu.ru - zadanie 230 spoj.sphere.pl - zadanie 44

spoj.sphere.pl - zadanie 70

2.6. Acykliczność

Maj¸ac dany graf G = (V, E) (skierowany b¸adź nieskierowany), można zadać pytanie, czy jest on acykliczny. Acykliczność grafu jest poż¸adan¸a własności¸a w wielu algorytmach.

Przykładowo, wyznaczanie topologicznego porz¸adku wierzchołków grafu skierowanego jest możliwe pod warunkiem, że graf, dla którego wyznaczany jest ten porz¸adek, jest acykliczny.

Opisany w poprzednim rozdziale algorytm zakłada prawdziwość tego faktu i nie sprawdza acykliczności grafu.

W przypadku grafów skierowanych, stosunkowo łatwo można Literatura [WDA] - 5.4 zmodyfikować algorytm służ¸acy do sortowania topologicznego

gra-fu, wzbogacaj¸ac go o dodatkowy test na acykliczność. Wystarczy pod koniec algorytmu sprawdzić, czy wszystkie krawędzie

prowa-dz¸a od wierzchołków znajduj¸acych się wcześniej, do wierzchołków znajduj¸acych się później w porz¸adku topologicznym. Implementacja tej metodologii jest zrealizowana w funkcji bool Graph<V,E>::AcyclicD() — do działania wymaga ona zaimplementowania funkcji void Graph<V,E>::TopoSort(), a zatem wykorzystuje również dodatkowe pole int t wierz-chołków. Implementacja tej funkcji znajduje się na listingu 2.21.

Listing 2.21: Implementacja funkcji bool Graph<V,E>::AcyclicD() // Funkcja sprawdzająca, czy dany graf skierowany jest acykliczny

1 bool AcyclicD() {

// Wyznacz sortowanie topologiczne 2 TopoSort();

// Dla każdej krawędzi w grafie sprawdź, czy prowadzi ona od wierzchołka // wcześniejszego do wierzchołka późniejszego w porządku topologicznym 3 FOREACH(it, g) FOREACH(it2, *it) if (it->t >= g[it2->v].t) return false;

4 return true;

5 }

Algorytm realizowany przez funkcję bool Graph<V,E>::AcyclicD()nie działa prawidłowo w przypadku grafów nieskierowanych — reprezentacja krawędzi (u, v) w takich grafach składa się z dwóch krawędzi skierowanych: (u, v), oraz (v, u). W podejściu realizowanym przez funkcję bool Graph<V,E>::AcyclicD(), każdy graf nieskierowany z co najmniej jedn¸a krawędzi¸a uważany jest za zawieraj¸acy cykl.

Dla grafów acyklicznych potrzebny jest inny algorytm. Funkcja bool Graph<V,E>::

AcyclicU(), której kod źródłowy został przedstawiony na listingu 2.22, jest implementacj¸a pomysłu działaj¸acego właśnie dla takich grafów. Wykonuje ona algorytm DFS dla wszyst-kich wierzchołków w grafie i w momencie powtórnego wejścia do tego samego wierzchołka stwierdza, że zawiera on cykl. Problem z reprezentacj¸a krawędzi nieskierowanych w postaci pary krawędzi skierowanych został rozwi¸azany poprzez nieprzetwarzanie krawędzi, po której algorytm wszedł do wierzchołka. Funkcja bool Graph<V,E>::AcyclicU() traktuje każd¸a pętlę oraz multikrawędzie jako cykl.

0

5 3 4 1 2

0

5 3 4 1 2

(a) (b)

Rysunek 2.6: (a) Nieskierowany graf acykliczny (b) Skierowany graf zawieraj¸acy cykl 5 → 4 → 0 → 5

Listing 2.22: Implementacja funkcji bool Graph<V,E>::AcyclicU() // Wskaźnik na tablicę służącą do odznaczania wierzchołków odwiedzonych 01 bool*vis;

// Zmienna, w której umieszczany jest wynik 02 boolacyc;

// Funkcja przeszukująca poddrzewo wierzchołka v w celu znalezienia cyklu 03 void AcDfs(intv, Ed * p) {

// Jeśli wierzchołek nie był jeszcze odwiedzony...

04 if (!vis[v]) { 05 vis[v] = 1;

// Przetwórz jego wszystkie krawędzie za wyjątkiem tej, po której przyszedłeś // od ojca

06 FOREACH(it, g[v]) if (&(*it) != p) AcDfs(it->v, &g[it->v][it->rev]);

07 } else acyc = 0;

08}

// Funkcja sprawdzająca, czy graf jest acykliczny - działa prawidłowo dla grafów // nieskierowanych

09 boolAcyclicU() {

// Inicjalizacja zmiennych 10 acyc = 1;

11 vis = new bool[SIZE(g)];

12 REP(x, SIZE(g)) vis[x] = 0;

// Dla każdego wierzchołka w grafie dokonaj przeszukiwania 13 REP(x, SIZE(g)) if (!vis[x]) AcDfs(x, 0);

14 delete[]vis;

15 return acyc;

16}

Złożoność czasowa obu przedstawionych w tym rozdziale funkcji to O(n + m). Tak samo jak w przypadku funkcji przedstawionych w poprzednich rozdziałach, implementacja jest pewn¸a modyfikacj¸a algorytmu DFS, która przetwarza każdy wierzchołek, oraz każd¸a krawędź grafu dokładnie raz. Dla grafu, przedstawionego na rysunku 2.6.a, funkcja boolGraph<V,E>

::AcyclicU()zwróci fałsz, natomiast funkcja bool Graph<V,E>::AcyclicD()dla grafu z rysunku 2.6.b — prawdę.