Algorytm Kosaraju do znajdowania silnie spójnych składowych w grafie ( http://www.algorytm.org/algorytmy-grafowe/silnie-spojne-skladowe.html – tu są 2 implementacje rekurencyjne i DOKŁADNY OPIS) zaimplementowałem w wersji bez rekurencji.
Dalej nie wygląda to za przejrzyście, ale jeśli ktoś ma problem ze rozumieniem rekurencji to może pomóc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | /* "Program znajdujący silnie spójne składowe w grafie skierowanym - bez funkcji rekurencyjnych" Jerzy Skalski (phoowned@wp.pl) 2017-05-15 Wymagania programu: C++11 (opcja kompilatora) - można przepisać pętle po vectorach i listach tak, żeby nie był wymagany Opis wejścia: W pierwszej linii wejścia znajduje się jedna liczba dodatnia n - liczba wierzchołków grafu. Dalej następuje n linii; i-ta spośród tych linii ma postać k a1 a2 ... ak gdzie a1, a2, ..., ak to numery wierzchołków, do których prowadzą krawędzie z i-tego, zaś k jest liczbą tych wierzchołków. Przykładowe dane: 5 1 1 3 2 3 4 1 0 0 0 Rysunek grafu skierowanego: /<---< 2 <---<\ / \ 0 >-------------> 1 >--> 3 \ \>-> 4 Wynik: SSS ID: 0 1 2 0 SSS ID: 1 3 SSS ID: 2 4 */ #include <iostream> #include <map> #include <vector> #include <stack> #include <list> using namespace std; int main() { int iloscWezlow, iloscKrawedziWezla, wezel, wezelSasiedni, wezelStartowy, idSilnieSkladowej, i; vector< int >* listaSasiadowGraf; vector< int >* listaSasiadowGrafTransponowany; list< int > kolejnoscOdwiedzaniaDFSem; stack< int > wezlyDoOdwiedzenia; unsigned int * odwiedzoneWezly; cin >> iloscWezlow; // inicjujemy tablice listaSasiadowGraf = new vector< int >[iloscWezlow]; listaSasiadowGrafTransponowany = new vector< int >[iloscWezlow]; odwiedzoneWezly = new unsigned int [iloscWezlow]; // ladujemy krawedzie do grafu i grafu transponowanego for (wezel = 0; wezel < iloscWezlow; ++wezel) { cin >> iloscKrawedziWezla; for (i = 0; i < iloscKrawedziWezla; ++i) { cin >> wezelSasiedni; listaSasiadowGraf[wezel].push_back(wezelSasiedni); listaSasiadowGrafTransponowany[wezelSasiedni].push_back(wezel); } } for (wezel = 0; wezel < iloscWezlow; ++wezel) odwiedzoneWezly[wezel] = 0; // przegladamy wezly grafu DFSem // zapisujemy czas przetworzenia (kolejnosc odwiedzania post order - dodajemy do listy jak sie 'cofamy') for (wezelStartowy = 0; wezelStartowy < iloscWezlow; ++wezelStartowy) { if (odwiedzoneWezly[wezelStartowy]) continue ; wezlyDoOdwiedzenia.push(wezelStartowy); while (!wezlyDoOdwiedzenia.empty()) { wezel = wezlyDoOdwiedzenia.top(); wezlyDoOdwiedzenia.pop(); if (odwiedzoneWezly[wezel]) { if (odwiedzoneWezly[wezel] == 1) { // push_front - powstanie lista od najwiekszego czasu przetworzenia kolejnoscOdwiedzaniaDFSem.push_front(wezel); odwiedzoneWezly[wezel] = 2; } continue ; } odwiedzoneWezly[wezel] = 1; wezlyDoOdwiedzenia.push(wezel); for ( int wezelSasiedni : listaSasiadowGraf[wezel]) { if (!odwiedzoneWezly[wezelSasiedni]) wezlyDoOdwiedzenia.push(wezelSasiedni); } } } for (wezel = 0; wezel < iloscWezlow; ++wezel) odwiedzoneWezly[wezel] = 0; // przegladamy wezly grafu transponowanego DFSem od najwiekszego czasu przetworzenia DFSem // wszystkie przejrzane w ten sposob wezly naleza do jednej silnie spójnej składowej idSilnieSkladowej = 0; for ( int wezelStartowy : kolejnoscOdwiedzaniaDFSem) { if (odwiedzoneWezly[wezelStartowy]) continue ; wezlyDoOdwiedzenia.push(wezelStartowy); cout << "SSS ID: " << idSilnieSkladowej << endl; // TUTAJ ZACZYNA SIE NOWA SSS while (!wezlyDoOdwiedzenia.empty()) { wezel = wezlyDoOdwiedzenia.top(); wezlyDoOdwiedzenia.pop(); if (odwiedzoneWezly[wezel]) { if (odwiedzoneWezly[wezel] == 1) { // przy ponownym wejsciu do wezla (cofajac sie DFS - post order) ustawiamy na 2 odwiedzoneWezly[wezel] = 2; cout << wezel << " " ; // TUTAJ NALEZY WYKONAC OPERACJE NA ELEMENCIE DANEJ SSS } continue ; } // przy pierwszym wejsciu do wezla ustawiamy na 1 odwiedzoneWezly[wezel] = 1; // dodajemy ten sam wezel ponownie na stos, zeby odwiedzic go podczas cofania wezlyDoOdwiedzenia.push(wezel); for ( int wezelSasiedni : listaSasiadowGrafTransponowany[wezel]) { if (!odwiedzoneWezly[wezelSasiedni]) wezlyDoOdwiedzenia.push(wezelSasiedni); } } cout << endl; // TUTAJ KONCZY SIE SSS idSilnieSkladowej++; } return 0; } |
Dla studentów którzy mają zadanie 'od babki’:
Jeśli chodzi o zadanie 'od babki’ to trzeba jeszcze dodać jednego 'for’ (może być z DFSem jak w drugim 'forze’ lub można w drugim 'forze’ wygenerować sobie 'mapę’ ze spisem wszystkich węzłów w każdej SSS) do policzenia ile każda silnie składowa spójna ma sąsiednich SSS, a potem jeszcze jeden 'for’ do wyświetlenia węzłów należących do tej SSS.
Algorytm który udostępniłem zakłada numerowanie węzłów od 0, a nie 1 jak w danych przykładowych od babki, więc należy przy wczytywaniu połączeń między węzłami zawsze zmieniać ’-1′ każde połączenie!