• Nie Znaleziono Wyników

Pochodzenie: Rozwi¸azanie:

Potyczki Algorytmiczne 2005 road.cpp

W Bajtocji jest n miast. Miasta s¸a poł¸aczone jednokierunkowymi drogami. Każda droga ł¸aczy tylko dwa miasta i nie przechodzi przez żadne inne. Niestety, nie zawsze z każdego miasta da się dojechać do każdego innego. Król Bajtazar postanowił rozwi¸azać ten problem.

Król ma świadomość, że budowanie nowych dróg jest bardzo kosztowne, a budżet Bajtocji nie jest zbyt zasobny. Dlatego też poprosił Cię o pomoc. Należy obliczyć minimaln¸a liczbę jednokierunkowych dróg, które trzeba zbudować, żeby z każdego miasta dało się dojechać do każdego innego miasta.

Zadanie

Napisz program, który:

ˆ wczyta opis istniej¸acej sieci dróg,

ˆ obliczy minimaln¸a liczbę dróg, które trzeba dobudować tak, aby z każdego miasta w Bajtocji dało się dojechać do każdego innego,

ˆ wypisze wynik.

Wejście

Pierwszy wiersz zawiera dwie liczby całkowite n i m (2 ¬ n ¬ 10 000, 0 ¬ m ¬ 100 000) oddzielone pojedynczym odstępem i oznaczaj¸ace, odpowiednio, liczbę miast i liczbę dróg w Bajtocji. Miasta s¸a ponumerowane od 1 do n. W każdym z kolejnych m wierszy znajduj¸a się dwie liczby całkowite, oddzielone pojedynczym odstępem. W i + 1 wierszu znajduj¸a się liczby ai i bi (1 ¬ ai, bi ¬ n dla 1 ¬ i ¬ m), reprezentuj¸a one jednokierunkow¸a drogę prowadz¸ac¸a z miasta ai do bi.

Wyjście

Pierwszy i jedyny wiersz wyjścia powinien zawierać dokładnie jedn¸a nieujemn¸a liczbę całkow-it¸a — minimaln¸a liczbę dróg, które trzeba zbudować w Bajtocji tak, aby z każdego miasta dało się dojechać do każdego innego miasta.

Przykład

acm.uva.es - zadanie 10731 acm.uva.es - zadanie 247 acm.uva.es - zadanie 125 spoj.sphere.pl - zadanie 51 acm.uva.es - zadanie 10510

2.5. Sortowanie topologiczne

Skierowany graf acykliczny (ang. directed acyclic graph), jest Literatura [WDA] - 23.4

[ASP] - 4.3.3 to graf, który nie posiada cykli. Innymi słowy, w grafie takim nie

istnieje para wierzchołków u i v poł¸aczonych ścieżkami u ; v i v ; u.

Sortowanie topologiczne skierowanego grafu G = (V, E) polega

na takim uporz¸adkowaniu wierzchołków ze zbioru V , aby dla każdej krawędzi (u, v) ∈ E, wierzchołek u znajdował się przed wierzchołkiem v. Sortowanie topologiczne daje się wyz-naczyć dla wszystkich grafów skierowanych nie zawieraj¸acych cykli. Do wykonania tego zada-nia można wykorzystać algorytm DFS — jedyne, co należy zrobić, to posortować wierzchołki w kolejności malej¸acych czasów przetworzenia f.

Załóżmy, że mamy dany skierowany acykliczny graf G oraz dwa wierzchołki u oraz v. Niech wartości f wyznaczone dla tych wierzchołków przez algorytm DFS będ¸a równe odpowiednio uf i vf, oraz załóżmy bez straty ogólności, że uf < vf. Zgodnie z naszym sposobem wyznacza-nia porz¸adku topologicznego, wierzchołek v zostanie umieszczony przed wierzchołkiem u.

Postępowanie takie jest zgodne z definicj¸a porz¸adku topologicznego, gdyż z warunku uf < vf oraz z acykliczności grafu G wynika, że w grafie tym nie ma krawędzi (u, v).

Algorytm DFS, oprócz zmiennych int f, wyznacza również inne, niepotrzebne z punktu widzenia sortowania topologicznego wartości, dlatego też implementacja funkcji void Graph

<V,E>::TopoSort()nie korzysta z przedstawionej wcześniej implementacji algorytmu DFS.

Wyznaczony porz¸adek topologiczny umieszczany jest w dodatkowych polach wierzchołków int t— pierwszy wierzchołek ma t¸a wartość równ¸a 0, drugi — 1... Listing 2.17 przedstawia implementację funkcji void Graph<V,E>::TopoSort().

0

5 3 4

1 2

0 1 4 3 2 5

(a) (b)

Rysunek 2.5: (a) Przykładowy skierowany graf acykliczny (b) jeden z możliwych porz¸adków topolog-icznych określonych na zbiorze wierzchołków grafu z rysunku (a)

Listing 2.17: Implementacja funkcji voidGraph<V,E>::TopoSort() 01 inttopo;

// Funkcja wykonująca algorytm DFS z wierzchołka v i aktualizująca wartości // zmiennych t

02 void TopoDfs(intv) {

// Jeśli wierzchołek nie był odwiedzony, to należy go odwiedzić 03 if (!g[v].t) {

// Zaznacz wierzchołek jako odwiedzony 04 g[v].t = 1;

// Odwiedź wszystkie wierzchołki, do których prowadzi krawędź z v 05 FOREACH(it, g[v]) TopoDfs(it->v);

// Zaktualizuj wartość t przetwarzanego wierzchołka 06 g[v].t = --topo;

07 } 08}

// Właściwa funkcja implementująca sortowanie topologiczne 09 void TopoSort() {

10 FOREACH(it, g) it->t = 0;

11 topo = SIZE(g);

// Odwiedź wszystkie wierzchołki w grafie 12 FORD(x, topo - 1, 0) TopoDfs(x);

13}

W wielu przypadkach, forma wyniku obliczana przez funkcję voidGraph<V,E>::TopoSort() jest nieporęczna w użyciu — często oczekiwanym rezultatem jest posortowana lista wierz-chołków grafu. Funkcja VI Graph<V,E>::TopoSortV(), której implementacja przedstaw-iona jest na listingu 2.18, jako wynik działania zwraca wektor liczb reprezentuj¸acych numery kolejnych wierzchołków w porz¸adku topologicznym.

Listing 2.18: Implementacja funkcjiVI Graph<V,E>::TopoSortV() 1 VI TopoSortV() {

2 VI res(SIZE(g));

// Wyznacz sortowanie topologiczne 3 TopoSort();

// Na podstawie wartości zmiennych t wierzchołków, wyznacz wektor z wynikiem 4 REP(x, SIZE(g)) res[g[x].t] = x;

Listing 2.18: (c.d. listingu z poprzedniej strony) 5 return res;

6 }

Czas działania algorytmu sortowania topologicznego to O(n + m), co wynika bezpośrednio z faktu, iż jest on modyfikacj¸a przeszukiwania grafu w gł¸ab.

Listing 2.19: Wynik wyznaczony przez funkcję VI Graph<V,E>::TopoSortV()dla grafu z ry-sunku 2.5.a

Kolejnosc topologiczna wierzcholkow: 0 1 3 4 2 5 Wierzcholek 0 ma pozycje 0 w porzadku topologicznym Wierzcholek 1 ma pozycje 1 w porzadku topologicznym Wierzcholek 2 ma pozycje 4 w porzadku topologicznym Wierzcholek 3 ma pozycje 2 w porzadku topologicznym Wierzcholek 4 ma pozycje 3 w porzadku topologicznym Wierzcholek 5 ma pozycje 5 w porzadku topologicznym

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

01 struct Ve {};

// Wzbogacenie wierzchołka o pole t, w którym umieszczany jest wynik 02 struct Vs {

03 intt;

04};

05 int main() { 06 intn, m, b, e;

// Wczytaj liczbę wierzchołków oraz krawędzi, stwórz graf o odpowiedniej wielkości

07 cin >> n >> m;

08 Graph<Vs, Ve> g(n);

// Dodaj do grafu wszystkie krawędzie skierowane 09 REP(x,m) {

10 cin >> b >> e;

11 g.EdgeD(b, e);

12 }

// Wyznacz porządek topologiczny oraz wypisz wynik 13 VI res = g.TopoSortV();

14 cout << "Kolejnosc topologiczna wierzcholkow: ";

15 FOREACH(it, res) cout << *it << " ";

16 cout << endl;

17 REP(x, SIZE(g.g)) cout << "Wierzcholek " << x << " ma pozycje " <<

18 g.g[x].t << " w porzadku topologicznym" << endl;

19 return0;

20}