Floyd Warshall

Algorytm Floyda-Warshalla oblicza najkrótszą drogę w grafie pomiędzy każda para wierzchołków w czasie O(n^3). Korzysta on z programowania dynamicznego, z nierówności trójkąta. Mając dane 3 wierzchołki u, v, k i chcąc obliczyć najkrótszą drogę miedzy u i v, skorzystamy ze wzoru odl[u, v]=min(odl[u, v], odl[u, k] + odl[k, v]). Algorytm testuje wiec tym wzorem wszystkie możliwe trójki wierzchołków (a jest ich n^3) i w ten sposób oblicza długość najkrótszej drogi miedzy każdą para wierzchołków w grafie. Podstawowa wersja algorytmu nie pozwala odtworzyć najkrótszej drogi a jedynie podaje jej długość, co jest dość istotną wadą. Można jednak temu zaradzić stosując tzw. macierz kierowania ruchem R[u][v]. Komórka R[u][v] przechowuje indeks wierzchołka pośredniego k przez który musimy przejść idąc optymalna droga u~>v. Wartości macierzy kierowania ruchem są aktualizowane w miarę przebiegu algorytmu. Potem, aby odtworzyć drogę u~>v należy użyć prostej funkcji rekurencyjnej, która korzysta z faktu ze drogi u->k i k->v mogą nie składać się z jednej krawędzi tylko z wielu, więc trzeba rozbić również je na u->k` i k`->k oraz k->k2` i k2`->v.

Przykładowy kod:

#include <iostream>
using namespace std;
int G[10000][10000]; //dlugosci krawedzi G[u][v];
int g[10000][10000]; //dlugosc najkrotszej sciezki u~>v
int R[10000][10000]; //macierz kierowania ruchem
const int INF=1000000001;
int N, M, u, v, w; //N-ilosc wierzcholkow; M-ilosc krawedzi
 
void init(){ //inicjuje graf i pozostale struktury wartosciami poczatkowymi
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            G[i][j]=INF;
            g[i][j]=INF;
            R[i][j]=-1;
        }
        G[i][i]=0;
        g[i][i]=0;
    }
}
 
void floyd(){ //znajduje najkrotsze sciezki miedzy kazda para wierzcholkow w grafie i wypelnia macierz kierowanie ruchem zeby mozna bylo odtworzyc przebieg kazdej drogi
    for(int k=0; k<N; k++){
        for(int i=0; i<N; i++){
            for(int j=0; j<N; j++){
                if(g[i][j]>g[i][k]+g[k][j]){ //jezeli droga z i do j, poprzez wierzcholek posredni k jest krotsza niz aktualnie najkrotsza znana trasa i->j to zaktualizuj
                    g[i][j]=g[i][k]+g[k][j];
                    R[i][j]=k; //oznacza to ze idac po sciezce i~>j trzeba przejsc przez k
                }
            }
        }
    }
}
 
void droga(int u, int v){ //odtwarza najkrotsza sciezke miedzy danymi wierzcholkami wykorzystujac macierz kierowania ruchem
    if(R[u][v]!=-1){ //dopoki nie dojdziemy do zwyklej krawedzi po ktorej trzeba wejsc to zchodz rekurencyjnie i wypisuj wierzcholek posredni k
        droga(u, R[u][v]);
        cout<<R[u][v]+1<<" ";
        droga(R[u][v], v);
    }
}
 
int main(){
    ios_base::sync_with_stdio(0);
    cin>>N>>M;
    init();
    for(int i=0; i<M; i++){
        cin>>u>>v>>w;
        G[u-1][v-1]=g[u-1][v-1]=w;
        G[v-1][u-1]=g[v-1][u-1]=w; //zakladamy ze graf jest nieskierowany; w przypadku skierowanego usunac ta linijke
    }
    floyd();
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cout<<i+1<<"->"<<j+1<<" dl. "<<g[i][j]<<", przebieg: "<<i+1<<" ";
            droga(i, j);
            cout<<j+1<<endl;
        }
    }
    system("pause");
    return 0;
}
//Przykladowe wejscie:
/*10 17
1 2 13
1 9 33
2 3 27
2 4 1
2 10 8
2 9 9
3 4 10
4 10 11
4 6 2
4 5 3
5 6 9
6 10 666
6 7 66
7 8 333
7 9 33
9 10 3
6 9 15*/
O ile nie zaznaczono inaczej, treść tej strony objęta jest licencją Creative Commons Attribution-ShareAlike 3.0 License