Gráfalgoritmusok ismétlés 2017. ősz
Gráfok ábrázolása Egy G = (V, E) gráf ábrázolására alapvetően két módszert szoktak használni: szomszédsági listákat, illetve szomszédsági mátrixot. A G = (V, E) gráf szomszédsági listás ábrázolása során egy A tömböt használunk. Az A tömb V darab listából áll; a gráf minden csúcsához egy lista tartozik. Minden u V csúcs esetén az A[u] szomszédsági lista azokat a v csúcsokat tartalmazza, amelyekre (u, v) E. Amikor egy G = (V, E) gráfot szomszédsági mátrixszal ábrázolunk, feltesszük, hogy a csúcsokat (tetszőleges módon) megszámozzuk az 1, 2,..., V értékekkel. Az ábrázoláshoz használt A = (a ij ) szomszédsági mátrix mérete V V és { 1 ha (i, j) E, a ij = 0 ha (i, j) E. Gráfok bejárása Gráfokkal kapcsolatos algoritmikus feladatokban gyakran van szükség arra, hogy az élek mentén lépkedve valamilyen szisztematikus módon végigjárjuk a gráf csúcsait. Két általános módszer ismert: a szélességi bejárás és a mélységi bejárás. Szélességi bejárás Tekintsünk egy G = (V, E) irányított vagy irányítatlan gráfot, és legyen s V egy kitüntetett csúcs. A szélességi bejárás módszeresen megvizsgálja G éleit, és rátalál az összes s-ből elérhető csúcsra, meghatározza az elérhető csúcsok távolságát s-től, továbbá létrehoz egy s gyökerű szélességi fát, amely tartalmazza az összes elérhető csúcsot; a szélességi fában az s-ből v-be vezető út a legrövidebb s-ből v-be vezető útnak felel meg G-ben bármely s-ből elérhető v csúcsra (legrövidebb út alatt most a legkevesebb élből álló utat értjük). Az algoritmus a már elért és a még felfedezetlen csúcsok közötti határvonalat egyenletesen terjeszti ki a határ teljes széltében. Először meglátogatjuk az s kezdőcsúcsot, majd ennek a csúcsnak az összes szomszédját. Aztán ezen szomszédok összes olyan szomszédját, ahol még nem jártunk, és így tovább. A szélességi bejárás algoritmus megvalósítható O( V + E ) költséggel. 1
Mélységi bejárás Egy G = (V, E) irányított gráf mélységi bejárásának stratégiája a következő. Egy kezdőcsúcsból kiindulva addig megyünk tovább irányított élek mentén, ameddig már nem tudunk még be nem járt csúcsba jutni. Ez esetben visszamegyünk az út utolsó előtti csúcsáig, és onnan próbálkozunk másfelé tovább lépni és előremenni. Ha ezek a lehetőségek is kimerültek, még eggyel visszamegyünk, és így tovább. Azt, hogy a v csúcsból már nem tudunk előre lépni, úgy fejezzük ki, hogy a v csúcs mélységi bejárása befejeződött. Előfordulhat, hogy már a kezdőcsúcs bejárását is befejeztük, de a gráfnak még mindig van olyan csúcsa, ahol nem jártunk. Ez azt jelenti, hogy a kezdőcsúcsunkból nem érhető el minden csúcs irányított úton. Ekkor egy új kezdőcsúcsot választunk a még be nem járt csúcsok közül, és innen folytatjuk a gráf mélységi bejárását. A mélységi bejárás algoritmus megvalósítható O( V + E ) költséggel. A mélységi bejárás algoritmus irányítatlan gráfokra is alkalmazható, hiszen egy irányítatlan gráf felfogható irányítottként úgy, hogy minden (u, v) irányítatlan élt az (u, v) és (v, u) irányított élekkel helyettesítünk. Erősen összefüggő komponensek Azt mondjuk, hogy egy G = (V, E) irányított gráf erősen összefüggő, ha bármely u, v V csúcsokra a gráfban vezet v-ből u-ba is és u-ból v-be is irányított út. Egy irányított gráf egy maximális erősen összefüggő részgráfját a gráf egy erősen összefüggő komponensének nevezzük. Más szavakkal, a G gráf egy H részgráfja a G egy erősen összefüggő komponense, ha H erősen összefüggő, továbbá G-nek nem létezik olyan H erősen összefüggő részgráfja, amelynek H valódi részgráfja lenne. Egyszerű, lineáris költségű eljárást adható a mélységi bejárás segítségével egy szomszédsági listákkal adott G = (V, E) irányított gráf erősen összefüggő komponenseinek feltérképezésére. Topológikus rendezés Legyen G = (V, E) tetszőleges irányított gráf, ahol V = n. A G gráf egy topológikus rendezése a csúcsainak egy olyan (v 1, v 2,..., v n ) permutációja (sorrendje), melyben (v i, v j ) E esetén i < j. Belátható, hogy egy irányított gráfnak akkor és csak akkor van topológikus rendezése, ha nem tartalmaz irányított kört. Ebben az esetben egyszerű, lineáris költségű topológikusan rendező eljárást kaphatunk a mélységi bejárás segítségével. 2
Legrövidebb utak Adott egy G = (V, E) irányított gráf, az élek halmazán egy w : E R súlyfüggvénnyel. A G gráf egy u csúcsát a v csúccsal összekötő (nem feltétlenül egyszerű) u v irányított út hossza legyen az úton szereplő élek súlyának összege. Legrövidebb u v úton egy olyan u v utat értünk, amelynek hossza minimális a G-beli u v utak között. Ezek után az u, v V csúcsok távolsága legyen 0 ha u = v, d(u, v) = ha nincs u v út, a legrövidebb u v út hossza különben. Vigyázat, itt u és v szerepe nem szimmetrikus, ha az egyik csúcs valamilyen távol van a másiktól, akkor nem biztos, hogy a másik is ugyanolyan távol van az egyiktől. A meghatározás nem is mindig értelmes: ha u és v olyan irányított körön vannak, amelynek összsúlya negatív, akkor ezen körözve akármilyen kicsi (negatív) úthosszt elérhetünk. Ezért a továbbiakban feltesszük, hogy G nem tartalmaz negatív összsúlyú irányított kört. A feladat: (1) Határozzuk meg egy adott csúcsból egy másik adott csúcsba menő legrövidebb utat. (2) Határozzuk meg egy adott csúcsból az összes többi csúcsba menő legrövidebb utakat. (3) Határozzuk meg az összes csúcsból az összes többi csúcsba menő legrövidebb utakat. Meglepő de az első két változat "ugyanolyan nehéz"; az ismert algoritmusok, amelyek megoldják az első problémát egyben megoldják a másodikat is. A legrövidebb utak keresésére szolgáló algoritmusok jellegzetesen a legrövidebb utak következő tulajdonságát aknázzák ki. Legyen G = (V, E) egy súlyozott élű, irányított gráf, továbbá P = (v 1, v 2,..., v k ) egy legrövidebb út a v 1 csúcsból a v k csúcsba. Ekkor tetszőleges 1 i < j k esetén a P út P ij = (v i, v i+1,..., v j ) részútja egy legrövidebb út a v i csúcsból a v j csúcsba. A következőkben feltesszük, hogy a G gráf az alábbi alakú C szomszédsági mátrixával adott: 0 ha u = v, C[u, v] = w(u, v) ha u v és (u, v) E, különben. 3
Dijkstra algoritmus Tegyük fel, hogy az élsúlyok nemnegatívak. Ekkor G nyilván nem tartalmaz negatív összsúlyú irányított kört. Jelöljünk ki a G = (V, E) gráfban egy s V csúcsot forrásnak. Célunk, hogy az összes u V csúcsnak az s csúcstól való távolságát meghatározzuk. Használni fogunk egy a G csúcsaival indexelt D tömböt. A D[u] értékekre úgy gondolhatunk, hogy azok minden időpillanatban az eljárás során addig megismert legrövidebb s u utak hosszát tartalmazzák. A D[u] mennyiségek mindenkor felső közelítései lesznek a keresett d(s, u) távolságoknak. Ezen kívül használunk még egy H halmazt, amelyben azokat a csúcsokat tároljuk, amelyek s-től mért távolságát már tudjuk. Dijkstra(G,s) H={s} for minden u V csúcsra do D[u]=C[s,u] for i=1 to V -1 do Válasszunk egy olyan x V\H csúcsot, amelyre D[x] minimális H=H {x} for minden v V\H csúcsra do D[v]=min{D[v],D[x]+C[x,v]} Az algoritmus költsége O( V 2 ). Legszélesebb út Legyen G = (V, E) egy súlyozott élű irányított gráf a w nem negatív súlyfüggvénnyel. Most az élsúlyok áteresztő képességeket reprezentálnak. A gráf egy u v irányított útjának szélessége legyen az út legkisebb súlyú élének súlya. Tetszőleges u és v csúcsokra legyen b(u, v) egy maximális szélességű u v irányított út szélessége (ha u-ból nem vezet út v-be, akkor legyen b(u, v) = 0 definíció szerint). Feladat a gráf egy s csúcsából a többi csúcsba menő legszélesebb irányított utak megtalálása. Használni fogunk egy a G csúcsaival indexelt B tömböt. A B[u] értékekre úgy gondolhatunk, hogy azok minden időpillanatban az eljárás során addig megismert legszélesebb s u utak szélessége. A B[u] mennyiségek mindenkor alsó közelítései lesznek a keresett b(s, u) szélességeknek. Ezen kívül használunk még egy H halmazt, amelyben azokat a csúcsokat tároljuk, amelyekhez vezető legszélesebb utak szélességét már tudjuk. 4
LegszélesebbUtak(G,s) H={s} for minden u V csúcsra do B[u]=C[s,u] for i=1 to V -1 do válasszunk egy olyan x V\H csúcsot, amelyre B[x] maximális H=H {x} for minden v V\H csúcsra do B[v]=max{B[v],min{B[x],C[x,v]}} Az algoritmus költsége O( V 2 ). Bellman-Ford algoritmus Most olyan módszert ismertetünk az egy csúcsból induló legrövidebb utak meghatározására, amely akkor is működik, ha bizonyos élsúlyok negatívak. Azt persze továbbra is feltesszük, hogy a gráf nem tartalmaz negatív összhosszúságú irányított kört. Az egyszerűség kedvéért legyen V = {1, 2,..., n} és s = 1. Minden 1 i n 1 és 1 j n esetén jelölje T [i, j] egy legrövidebb olyan 1 j út hosszát, amely legfeljebb i élből áll. Nyilván T [1, j] = C[1, j] minden 1 j n esetén. Ha pedig i > 1, akkor T [i, j] = min{t [i 1, j], min{t [i 1, k] + C[k, j] 1 k n, k j}}. Jegyezzük még meg, hogy ha egy súlyozott élű G = (V, E) irányított gráfban nincs negatív összhosszúságú irányított kör, akkor bármely két csúcs között van olyan legrövidebb út is, ami egyszerű, azaz nem tartalmaz ismétlődő csúcsokat (a körök az út hosszának növelése nélkül eltávolíthatók). Így bármely két csúcs között van olyan legrövidebb út is, amely legfeljebb V 1 élből áll. Következésképpen T [n 1, j] egy legrövidebb 1 j út hosszát tartalmazza minden 1 j n esetén. Mindezek után az algoritmus már egyszerű: a rekurzív képlet szerint sorfolytonosan töltsük ki a T táblázatot, először az első sort balról jobbra, aztán a másodikat, és így tovább. A T táblázatban egy adott érték kiszámításának költsége nyilván O( V ), így az algoritmus összköltsége O( V 3 ). Negatív összsúlyú irányított kör A feladat hatékony algoritmust adni annak eldöntésére, hogy egy súlyozott élű irányított gráf tartalmaz-e negatív összsúlyú irányított kört! 5
Legyen G = (V, E) egy súlyozott élű irányított gráf a w : E R súlyfüggvénnyel. Legyenek a G gráf csúcsai v 1, v 2,..., v n. Vegyünk fel egy új s csúcsot, és vezessünk s-ből nulla súlyú irányított éleket a v 1, v 2,..., v n csúcsokba. Az így kialakult gráfot jelölje G. Világos, hogy G-ben akkor és csak akkor van negatív összsúlyú irányított kör, ha ugyanez fennáll G -re is. Futtassuk le G -re a Bellman-Ford algoritmust az s kezdőcsúccsal. Az algoritmus által szolgáltatott távolságértékeket jelölje d[v 1 ], d[v 2 ],..., d[v n ]. Vegyük észre, hogy most d[v i ] 0 minden 1 i n esetén. Vizsgáljuk meg ezután minden (v i, v j ) E élre, hogy teljesül-e a d[v j ] d[v i ] + w(v i, v j ) összefüggés. Ha G -ben nincs negatív összsúlyú irányított kör, akkor a d[v 1 ], d[v 2 ],..., d[v n ] értékek rendre az s-ből a v 1, v 2,..., v n csúcsokba vezető legrövidebb utak hosszai, így a fenti összefüggéseknek nyilvánvalóan teljesülniük kell. Továbbá nem nehéz belátni, hogy ha G -ben van negatív összsúlyú irányított kör, akkor van olyan (v i, v j ) E él, amelyre nem teljesül. Az algoritmus költsége O( V 3 ). Floyd-Warshall algoritmus d[v j ] d[v i ] + w(v i, v j ) A következőkben azt vizsgáljuk, hogy miként lehet egy irányított gráfban az összes csúcspár távolságát meghatározni. Nemnegatív élsúlyok esetén nyilvánvaló, hogy ha a Dijkstra algoritmust minden csúcsra mint forrásra lefuttatjuk, akkor az összes (rendezett) csúcspár távolságát megkapjuk. Van azonban egy ennél közvetlenebb módszer, amelynek előnye, hogy akkor is működik, ha van a gráfban negatív élsúly (természetesen negatív összhosszúságú irányított kör továbbra sem lehet). Az egyszerűség kedvéért most is legyen V = {1, 2,..., n}. Minden 1 i, j n és 0 k n esetén jelölje F k [i, j] egy legrövidebb olyan i j út hosszát, amelynek közbülső csúcsai k-nál nem nagyobb sorszámúak. Nyilván F 0 [i, j] = C[i, j] minden 1 i, j n esetén. Ha pedig k > 0, akkor F k [i, j] = min{f k 1 [i, j], F k 1 [i, k] + F k 1 [k, j]}}. Egy legrövidebb i j út hosszát F n [i, j] tartalmazza minden 1 i, j n esetén. Az algoritmus költsége O( V 3 ). 6
Minimális költségű feszítőfák Legyen G = (V, E) egy irányítatlan, összefüggő gráf. A G gráf egy körmentes, összefüggő F = (V, E ) részgráfja a gráf egy feszítőfája. A feszítőfa tehát egy olyan fa, amely G minden csúcsát tartalmazza, és élei a G gráf élei közül valók. Tegyük fel, hogy G élein értelmezve van egy w : E R súlyfüggvény. Most a G gráf egy F feszítőfáját minimális költségűnek nevezzük, ha költsége a benne szereplő élek súlyainak összege minimális G összes feszítőfája közül. A feladat G egy minimális költségű feszítőfájának meghatározása. A minimális feszítőfák keresésére szolgáló algoritmusok legtöbbjének közös vonása, hogy valamilyen módon sorra veszik a gráf éleit, és bizonyosakat bevesznek a kialakuló minimális költségű feszítőfába, másokat pedig eldobnak. A módszerek működése szemlélhető úgy, mintha a gráf éleit színeznénk. A gráf élei kezdetben színtelenek. Minden lépésben kiválasztunk egy még színtelen élt és azt kékre, illetve pirosra színezzük. Végül a kék élek egy minimális költségű feszítőfát adnak. Prim algoritmus. Legyen s a G gráf egy rögzített csúcsa. Minden színező lépéssel az s-et tartalmazó F kék fát bővítjük. Kezdetben az F csúcshalmaza {s}, végül pedig az egész V. A következő kék élnek mindig az egyik legkisebb súlyú élet választjuk azok közül, amelyek F -beli csúcsból F -en kívüli csúcsba mennek. Kruskal algoritmus. A következő befestendő f él legyen mindig a legkisebb súlyú színezetlen él. Ha f két végpontja ugyanabban a kék fában van, akkor az él legyen piros, különben pedig kék. Mindkét algoritmus megvalósítható O( E log E ) költséggel. Minimális költségű feszítőfa variáció Egy feszítőfa költségét úgy definiáltuk, mint a fában szereplő élek súlyainak összege. Bizonyos esetekben másféle költségszámítás is értelmes lehet, például egy fa költségeként értelmezhetjük a legnagyobb súlyú élének a súlyát. Megmutatható, hogy ha egy G = (V, E) súlyozott élű, irányítatlan, összefüggő gráfban T minimális költségű feszítőfa az előbbi értelemben, akkor minimális költségű feszítőfa az utóbbi értelemben is! Néhány NP-teljes feladat Végül lássunk pár olyan problémát, amelyekre nem ismert hatékony algoritmus. 7
(1) Egy gráfot k színnel színezhetőnek nevezünk, ha a csúcsaihoz i egészeket (színeket) rendelhetünk úgy, hogy 1 i k, és ha két csúcsot él köt össze, akkor a hozzájuk rendelt színek különbözők. Döntsük el, hogy egy gráf 3 színnel színezhető-e! Döntsük el, hogy egy gráf 4 színnel színezhető-e!... (2) A G irányítatlan gráf csúcsainak egy S részhalmazát független halmaznak nevezzük, ha semelyik két S-beli csúcs között nem fut él G-ben. Döntsük el egy adott G gráfról és egy adott k pozitív egész számról, hogy a G gráfnak van-e legalább k elemű független csúcshalmaza! (3) A G irányítatlan gráf csúcsainak egy T részhalmazát klikknek nevezzük, ha bármely két T -beli csúcs között fut él G-ben. Döntsük el egy adott G gráfról és egy adott k pozitív egész számról, hogy a G gráf tartalmaz-e legalább k elemű klikket! (4) A G irányítatlan gráf csúcsainak egy U részhalmazát éllefogó halmaznak nevezzük, ha G bármely élének van U-beli végpontja. Döntsük el egy adott G gráfról és egy adott k pozitív egész számról, hogy a G gráfnak van-e legfeljebb k elemű éllefogó csúcshalmaza! (5) A G irányított gráf egy irányított köre Hamilton kör, ha abban G minden csúcsa pontosan egyszer szerepel. Döntsük el egy adott G gráfról, hogy tartalmaz-e Hamilton kört! (6) A G irányított gráf egy irányított útja Hamilton út, ha abban G minden csúcsa pontosan egyszer szerepel. Döntsük el egy adott G gráfról, hogy tartalmaz-e Hamilton utat! 8