Algorytmy grafowe 2 Andrzej Jastrz bski Akademia ETI
Minimalne drzewo spinaj ce Drzewem nazywamy spójny graf nie posiadaj cy cyklu. Liczba wierzchoªków drzewa jest o jeden wi ksza od liczby jego kraw dzi. Drzewem spinaj cym nazywamy podgraf grafu G, który jest drzewem i zawiera wszystkie wierzchoªki grafu G. Je±li dany jest graf z wagami, to drzewo spinaj ce o najmniejszej sumie wag na kraw dziach b dziemy nazywali minimalnym drzewem spinaj cym.
Algorytm Prima mindrzewoprim(graf G) { T - graf bez kraw dzi i wierzchoªków Dodajemy do T dowolny wierzchoªek z G while( V(T) < V(G) ) { szukamy najl»ejszej kraw dzi pomi dzy wierzchoªkami z V(T) i V(G)/V(T) dodajemy wyszukan kraw d¹ wraz z incydentnym wierzchoªkiem z V(G)/V(T) do grafu T return T;
Alorytm Prima - szczegóªy implementacyjne Najwa»niejsz cz ±ci, jest algorytm wyznaczania kolejnej - najl»ejszej kraw dzi pomi dzy wierzchoªkami z V(T) i V(G)/V(T). Mo»emy stworzy kolejk priorytetow maj c jako elementy: wag najl»ejszej kraw dzi do T oraz s siada (wierzchoªek) z T poª czonego najl»ejsz kraw dzi. Stworzymy kolejk priorytetow na podstawie kopca.
Kolejka priorytetowa struct Sasiad { int idkolejka, prevwierz, waga; Sasiad(): idkolejka(-1), prevwierz(0), waga(0) { ; B dziemy rozpatrywali tablic struktur Sasiad. idkolejka wskazuje na miejsce w kopcu, gdzie znajduje si wierzchoªek; je±li idkolejka jest równa -1, to wierzchoªek jeszcze nie przebywaª w kolejce priorytetowej; je±li idkolejka jest równa -2, to wierzchoªek byª ju» w kolejce priorytetowej; waga waga najl»ejszej kraw dzi, która ª czy wierzchoªek opisany przez struktur Sasiad z wierzchoªkiem z grafu T; prevwierz wskazuje na wierzchoªek z grafu T, który s siaduje z wierzchoªkiem opisanym przez struktur Sasiad najl»ejsz kraw dzi.
Kolejka priorytetowa void fixheapup(int id, vector<int> &kopiec, vector<sasiad> &sasiad) { while(id>0) { int uid = (id-1)>>1; if( sasiad[ kopiec[id] ].waga >=sasiad[ kopiec[uid] ].waga ) return; int tmp = kopiec[id]; kopiec[id] = kopiec[uid]; kopiec[uid] = tmp; sasiad[kopiec[id]].idkolejka = id; sasiad[kopiec[uid]].idkolejka = uid; id = uid;
Kolejka priorytetowa void fixheapdown(vector<int> &kopiec, vector< Sasiad > &sasiad) { int id = 0, uid = 1; while(uid<kopiec.size()) { int minid = id; if(kopiec[minid].waga > kopiec[uid].waga) minid = uid; uid++; if(uid<kopiec.size() && kopiec[minid].waga > kopiec[uid].waga) minid = uid; if(minid==id) return; int tmp = kopiec[id]; kopiec[id] = kopiec[minid]; kopiec[minid] = tmp; sasiad[kopiec[id]].idkolejka = id; sasiad[kopiec[minid]].idkolejka = minid; id = minid; uid = (minid<<1)+1;
Kolejka priorytetowa int getminval(vector<int> &kopiec, vector< Sasiad > &sasiad) { int odp = kopiec[0]; sasiad[ odp ].idkolejka = -2; if(kopiec.size()==1) return odp; kopiec[0] = kopiec.back(); kopiec.pop_back(); sasiad[ kopiec[0] ].idkolejka = 0; fixheapdown(kopiec, sasiad); return odp;
Algorytm Prima struct Krawedz { int sasiad, waga; ; int mindrzewoprim(vector< list<krawedz> > G) { vector<sasiad> sasiad; sasiad.resize(g.size()); //ustawiamy 0 wierzchoªek jako korze«sasiad[0].idkolejka = -2; list<krawedz>::iterator beg, end=g[0].end(); for(beg=g[0].begin(); beg!=end; beg++) { int pos = kopiec.size(); sasiad[ beg->sasiad ].waga = beg->waga; sasiad[ beg->sasiad ].prevwierz = 0; sasiad[ beg->sasiad ].idkolejka = pos; kopiec.push_back( beg->sasiad ); fixheapup(pos, kopiec, sasiad);.
Algorytm Prima cd. while(kopiec.size()>0) { int minwierz = getminval(kopiec, sasiad); end = G[minWierz].end(); for(beg=g[minwierz].begin(); beg!=end; beg++) { if( sasiad[beg->sasiad].idwierz == -2 ) continue; int pos; if( sasiad[beg->sasiad].idwierz == -1 ) { pos = kopiec.size(); sasiad[ beg->sasiad ].idwierz = pos; kopiec.push_back( beg->sasiad ); else if(sasiad[beg->sasiad].waga>beg->waga) pos = sasiad[beg->sasiad].idkolejka; else continue; sasiad[ beg->sasiad ].waga = beg->waga; sasiad[ beg->sasiad ].prevwierz = minwierz; fixheapup(pos, kolejka, sasiad);
Dyskusja o algorytmie Prima Powy»szy algorytm nic nie zwraca, ale w zale»no±ci od potrzeby mo»emy otrzyma wag najl»ejszego drzewa: int suma = 0; for(i=1; i<sasiad.size(); i++) //sic! suma += sasiad[i].waga; albo drzewo. Zªo»ono± obliczeniowa tego algorytmu to O(m log n), gdzie n= V(G), m= E(G). Je±li u»yliby±my kopca Fibonacciego jako kolejki priorytetowej zªo»ono± wyniosªaby O(m + n log n).
Algorytm Kruskala mindrzewokruskal(graf G) { sortujemy kraw dzie grafu G ze wzgl du na wag T - graf bez kraw dzi z V(G) wierzchoªkami foreach(e in posortowane kraw dzie) { je±li dodanie e do T nie tworzy cyklu w grafie T, to dodajemy kraw d¹ e do T return T;
Algorytm Kruskala Sprawdzenie, czy dodanie kraw dzi doprowadzi do powstania cyklu, mo»na realizowa przez lasy zbiorów rozª cznych.
Lasy zbiorów rozª cznych Lasy zbiorów rozª cznych mo»na zaimplementowa za pomoc tablicy. Podstawow operacj b dzie wyszukiwanie korzenia z kompresj ±cie»ki. int znajdzkorzen(int v, vector<int> &las) { if(las[v]==v) return v; return las[v] = znajdzkorzen(las[v], las); Tablica las przechowuje odniesienie do ojca w zªa. Operacja znajdzkorzen powoduje spªaszczenie drzewa przy ka»dym wywoªaniu.
Dziaªanie lasów zbiorów rozª cznych TAblica
Algorytm Kruskala struct KrawedzKruskal { int u, v, waga; KrawedzKruskal(int tu, int tv, int tw): u(tu), v(tv), waga(tw) friend bool operator<(const KrawedzKruskal &a, const KrawedzKruskal &b) { return a.waga<b.waga; int mindrzewokruskal(vector< list<krawedz> > G) { list<krawedz>::iterator beg, end; vector<krawedzkruskal> sortkraw; for(int i=0; i<g.size(); i++) { beg = G[i].begin(); end = G[i].end(); for(; beg!=end; beg++) sortkraw.push_back( KrawedzKruskal(i,beg->sasiad,beg->waga) ); sort(sortkraw.begin(), sortkraw.end());.
Algorytm Kruskala. vector<int> las; las.resize(g.size()); for(int i=0; i<las.size(); i++) las[i]=i; int sumawag=0; for(int i=0; i<sortkraw.size(); i++) { int korzenu = znajdzkorzen(sortkraw[i].u, las); int korzenv = znajdzkorzen(sortkraw[i].v, las); if(korzenu==korzenv) continue; sumawag += sortkraw[i].waga; las[korzenu] = korzenv; return sumawag;
Przepªywy w sieciach Mamy dany graf skierowany G z dodatnimi wagami (przepustowo± ). Wyró»nijmy dwa wierzchoªki s i t. Przepustowo± b dziemy oznaczali przez c(, ). Je±li pomi dzy wierzchoªkami u, v nie ma kraw dzi, to c(u, v) = 0. Tak graf b dziemy nazywali sieci. Przepªywem nazywamy funkcj f : V V R speªniaj c : warunek przepustowo±ci: dla ka»dych u, v V f (u, v) c(u, v) warunek sko±nej symetryczno±ci: dla ka»dych u, v V f (u, v) = f (v, u) warunek zachowania przepªywu: dla ka»dego u V \{s, t f (u, v) = 0 v V
8 1 s 2 2 3 t 9 8 1
Warto± przepªywu, maksymalny przepªyw Warto±ci przepªywu jest liczba f (s, v). v V Maksymalnym przepªywem sieci G jest przepªyw o maksymalnej warto±ci przepªywu.
Sie residualna Dla danej sieci G = (V, E) oraz przepªywu f (, ) mo»na stworzy przepustowo±ci residualne równe c f (u, v) = c(u, v) f (u, v) oraz sie residualn H = (V, E ), gdzie E = {(u, v) V 2 : c f (u, v) > 0. 8 8 1 2 1 2 s 2 2 3 t s 2 2 5 2 t 9 8 9 2 8 1 3
cie»ka powi kszaj ca cie»k powi kszaj c sieci G jest droga (prosta) pomi dzy wierzchoªkami s oraz t. 8 8 1 2 1 2 s 2 2 3 t s 2 2 5 2 t 9 8 9 2 8 1 3
Metoda FulkersonaForda FulkFordMetod(G, s, t) { foreach (u,v) in E(G) f(u,v)=f(v,u)=0; while( istnieje ±cie»ka powi kszaj ca pomi dzy s i t w sieci residualnej G f ) { p = ±cie»ka powi kszaj ca; c - minimalna warto± c f na ±cie»ce p; foreach (u,v) in p { f(u,v) += c; f(v,u) -= c;
Algorytm Edmondsa-Karpa Algorytm Edmondsa-Karpa jest realizacj metody Fulkersona-Forda. Algorytm znajduje najkrótsz ±cie»k powi kszaj c (pod wzgl dem liczby wierzchoªków).
8 1 s 2 2 3 t 9 8 1
8 1 s 2 2 3 t 9 8 1
5 s 2 2 3 t 9 8 1
5 s 2 2 3 t 9 8 1
5 s 2 2 3 t 5 5
5 s 2 2 3 t 5 5
2 7 s 6 1 t 6 6 3 2 5