|
Serwis Edukacyjny w I-LO w Tarnowie
Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2026 mgr Jerzy Wałaszek
|
ProblemW spójnym grafie ważonym należy znaleźć wagi najkrótszych ścieżek pomiędzy wszystkimi parami wierzchołków. |
Zadanie to rozwiązuje algorytm Floyda-Warshalla. Działa on w sposób dynamiczny i opiera się na spostrzeżeniu, że jeśli koszt dojścia z wierzchołka v do u jest większy od sumy kosztów dojść z wierzchołka v do k i z k do u, to za lepszy koszt należy przyjąć tę nową, mniejszą wartość.

Przetwarzany graf może posiadać krawędzie o wagach ujemnych, jednakże nie mogą w nim występować cykle ujemne (ang. negative cycles – cykle, w których suma wag krawędzi jest ujemna). Algorytm Floyda-Warshalla może takie cykle wykrywać.
Algorytm w podstawowej wersji tworzy macierz d o rozmiarzePrześledźmy na prostym przykładzie działanie tego algorytmu.
![]() |
|
Oto nasz graf ważony z ujemnymi wagami krawędzi. Przygotowujemy macierz d. Na głównej przekątnej umieszczamy 0, a w pozostałych komórkach umieszczamy ∞. |
||||||||||||||||||||||||||||||||||||
![]() |
|
Następnie dla każdej krawędzi u–v grafu w komórce d[u,v] umieszczamy wagę krawędzi w (u–v). Wartość d[i,j] = ∞ oznacza, że wierzchołek i-ty nie łączy się krawędzią z wierzchołkiem j-tym (ale, jak zobaczymy dalej, może istnieć ścieżka łącząca te wierzchołki, a wtedy algorytm wprowadzi do d[i,j] jej koszt). |
||||||||||||||||||||||||||||||||||||
![]() |
|
Rozpoczynamy przeglądanie kolejnych wierzchołków grafu od wierzchołka k = 0. Dla każdej pary wierzchołków u, v sprawdzamy, czy zachodzi nierówność: d[u,v] > d[u,k]+d[k,v]. Jeśli tak, to d[u,v] ← d[u,k] + d[k,v]. W tym przypadku nierówność będzie spełniona dla par wierzchołków: (1,3): d[1,3] = ∞ > d[1,0]+d[0,3] = -4+8 = 4 (3,2): d[3,2] = ∞ > d[3,0]+d[0,2] = -1+4 = 3 Zmieniamy dla nich wpisy w macierzy d: d[1,3] ← 4 d[3,2] ← 3 |
||||||||||||||||||||||||||||||||||||
![]() |
|
Idziemy do następnego wierzchołka grafu, k = 1. Znów sprawdzamy podaną wyżej nierówność dla wszystkich par wierzchołków w grafie. Będzie spełniona dla p: (0,2): d[0,2] = 4 > d[0,1]+d[1,2] = 5+-2 = 3 (0,4): d[0,4] = ∞ > d[0,1]+d[1,4] = 5+ 5 = 10 (3,0): d[3,0] = -1 > d[3,1]+d[1,0] = 2+-4 = -2 (3,2): d[3,2] = 3 > d[3,1]+d[1,2] = 2+-2 = 0 Ustawiamy: d[0,2] ← 3 d[0,4] ← 10 d[3, 0] ← -2 d[3, 2] ← 0 |
||||||||||||||||||||||||||||||||||||
![]() |
|
Następny wierzchołek, k = 2. Nierówność jest spełniona dla następujących par wierzchołków: (0,4): d[0,4] = 10 > d[0,2]+d[2,4] = 3+2 = 5 (1,3): d[1,3] = 4 > d[1,2]+d[2,3] = -2+5 = 3 (1,4): d[1,4] = 5 > d[1,2]+d[2,4] = -2+2 = 0 Ustawiamy: d[0,4] ← 5 d[1,3] ← 3 d[1,4] ← 0 |
||||||||||||||||||||||||||||||||||||
![]() |
|
Następny wierzchołek, k = 3. (2,0): d[2,0] = ∞ > d[2,3]+d[3,0] = 5+-2 = 3 (2,1): d[2,1] = ∞ > d[2,3]+d[3,1] = 5+ 2 = 7 (4,0): d[4,0] = ∞ > d[4,3]+d[3,0] = 2+-2 = 0 (4,1): d[4,1] = ∞ > d[4,3]+d[3,1] = 2+ 2 = 4 (4,2): d[4,2] = 4 > d[4,3]+d[3,2] = 2+ 0 = 2 Ustawiamy: d[2,0] ← 3 d[2,1] ← 7 d[4,0] ← 0 d[4,1] ← 4 d[4,2] ← 2 |
||||||||||||||||||||||||||||||||||||
![]() |
|
Ostatni wierzchołek, k = 4. (0,3): d[0,3] = 8 > d[0,4]+d[4,3] = 5+2 = 7 (1,3): d[1,3] = 3 > d[1,4]+d[4,3] = 0+2 = 2 (2,0): d[2,0] = 3 > d[2,4]+d[4,0] = 2+0 = 2 (2,1): d[2,1] = 7 > d[2,4]+d[4,1] = 2+4 = 6 (2,3): d[2,3] = 5 > d[2,4]+d[4,3] = 2+2 = 4 Ustawiamy: d[0,3] ← 7 d[1,3] ← 2 d[2,0] ← 2 d[2,1] ← 6 d[2,3] ← 4 |
Koszty dojść są następujące:
d[0,0] = 0 d[0,1] = 5 d[0,2] = 3 d[0,3] = 7 d[0,4] = 5 d[1,0] = -4 d[1,1] = 0 d[1,2] = -2 d[1,3] = 2 d[1,4] = 0 d[2,0] = 2 d[2,1] = 6 d[2,2] = 0 d[2,3] = 4 d[2,4] = 2 d[3,0] = -2 d[3,1] = 2 d[3,2] = 0 d[3,3] = 0 d[3,4] = -1 d[4,0] = 0 d[4,1] = 4 d[4,2] = 2 d[4,3] = 2 d[4,4] = 0
K01: Utwórz macierz d o rozmiarze n × n K02: Dla i = 0, 1, …, n-1: wykonuj kroki K03…K05 K03: Dla j = 0, 1, …, n-1: ; ustawiamy wszystkie wykonuj: d[i,j] ← ∞ ; komórki na nieskończoność K04: d[i,i] ← 0 ; elementy głównej przekątnej zerujemy K05: Dla każdego sąsiada j wierzchołka i: ; ustawiamy w d wagi krawędzi wykonuj: d[i,j] ← waga krawędzi i–j K06: Dla k = 0, 1, …, n-1, ; przechodzimy przez wykonuj kroki K07…K09 ; wszystkie wierzchołki grafu K07: Dla i = 0, 1, …, n-1, ; wybieramy wszystkie pary wykonuj kroki K08…K09 ; i, j wierzchołków w grafie K08: Dla j = 0, 1, …, n-1, wykonuj krok K09 K09: Jeśli d[i,j] > d[i,k]+d[k,j], ; testujemy nierówność i, to: d[i,j] ← d[i,k]+d[k,j] ; jeśli jest spełniona, modyfikujemy koszt K10: Zakończ z wynikiem d
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Poniższy program najniższe koszty
dojścia dla wszystkich par wierzchołków w grafie za pomocą algorytmu
Floyda-Warshalla. Definicja danych wejściowych jest następująca: W pierwszym wierszu program odczytuje kolejno liczbę wierzchołków n oraz liczbę krawędzi m. W m kolejnych wierszach znajdują się definicje krawędzi. Definicja składa się z trzech liczb x y w. Pierwsza liczba x oznacza początek krawędzi, druga liczba y oznacza koniec krawędzi, a trzecia liczba w jest wagą tej krawędzi. Na wyjściu program wypisuje koszt najkrótszej ścieżki pomiędzy każdymi dwoma wierzchołkami grafu. Jeśli ścieżka nie istnieje, pojawia się napis "NO PATH". |
![]() |
5 13 0 1 5 0 2 4 0 3 8 1 0 -4 1 2 -2 1 4 5 2 3 5 2 4 2 3 0 -1 3 1 2 3 4 -1 4 2 4 4 3 2 |
Pascal// Algorytm Floyda-Warshalla
// Data: 20.04.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program floyd_warshall;
// Typ danych
// dla macierzy dynamicznej
type
nptr = array of integer;
var
// Macierz minimalnych
// kosztów dojścia
d : array of nptr;
i,j,k,n,m,x,y,w : integer;
begin
// Czytamy liczbę
// wierzchołków oraz krawędzi
read(n, m);
// Tworzymy dynamiczną
// macierz d
SetLength(d, n);
for i := 0 to n-1 do
begin
// Tworzymy wiersz macierzy
SetLength(d[i], n);
for j := 0 to n-1 do
// Wiersz wypełniamy
// największą liczbą
// dodatnią
d[i][j] := MAXINT;
// Jednakże na przekątnej
// wpisujemy 0
d[i][i] := 0;
end;
for i := 1 to m do
begin
// Czytamy definicję krawędzi
read(x, y, w);
// Wagę krawędzi
// umieszczamy w macierzy d
d[x][y] := w;
end;
// Algorytm Floyda-Warshalla
for k := 0 to n-1 do
for i := 0 to n-1 do
for j := 0 to n-1 do
begin
if (d[i][k] = MAXINT) or
(d[k][j] = MAXINT) then
continue;
w := d[i][k]+d[k][j];
if d[i][j] > w then
d[i][j] := w;
end;
// Wyświetlamy wyniki
writeln;
for i := 0 to n-1 do
for j := 0 to n-1 do
begin
write('d[',i,',',j,'] = ');
if d[i][j] = MAXINT then
writeln('NO PATH')
else
writeln(d[i][j]);
end;
// Usuwamy macierz dynamiczną
for i := 0 to n-1 do
SetLength(d[i], 0);
SetLength(d, 0);
end.
|
// Algorytm Floyda-Warshalla
// Data: 20.04.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
using namespace std;
// "plus nieskończoność"
const int MAXINT = 2147483647;
int main()
{
// Macierz minimalnych
// kosztów dojścia
int ** d;
int i,j,k,n,m,x,y,w;
// Czytamy liczbę
// wierzchołków oraz krawędzi
cin >> n >> m;
// Tworzymy dynamiczną
// macierz d
d = new int * [n];
for(i = 0; i < n; i++)
{
// Tworzymy wiersz macierzy
d[i] = new int [n];
for(j = 0; j < n; j++)
// Wiersz wypełniamy
// największą liczbą
// dodatnią
d[i][j] = MAXINT;
// Jednakże na przekątnej
// wpisujemy 0
d[i][i] = 0;
}
for(i = 0; i < m; i++)
{
// Czytamy
// definicję krawędzi
cin >> x >> y >> w;
// Wagę krawędzi
// umieszczamy w macierzy d
d[x][y] = w;
}
// Algorytm Floyda-Warshalla
for(k = 0; k < n; k++)
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
if((d[i][k] == MAXINT) ||
(d[k][j] == MAXINT))
continue;
w = d[i][k]+d[k][j];
if(d[i][j] > w)
d[i][j] = w;
}
// Wyświetlamy wyniki
cout << endl;
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
cout << "d[" << i
<< "," << j
<< "] = ";
if(d[i][j] == MAXINT)
cout << "NO PATH";
else
cout << d[i][j];
cout << endl;
}
// Usuwamy macierz dynamiczną
for(i = 0; i < n; i++)
delete [] d[i];
delete [] d;
return 0;
}
|
Basic' Algorytm Floyda-Warshalla
' Data: 20.04.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
' "plus nieskończoność"
const MAXINT = 2147483647
' Macierz minimalnych
' kosztów dojścia
Dim As Integer Ptr Ptr d
Dim As Integer i,j,k,n,m,x,y,w
Open Cons For Input As #1
' Czytamy liczbę
' wierzchołków
' oraz krawędzi
Input #1, n, m
' Tworzymy
' dynamiczną
' macierz d
d = New Integer Ptr [n]
For i = 0 To n-1
' Tworzymy
' wiersz macierzy
d[i] = New Integer [n]
For j = 0 To n-1
' Wiersz wypełniamy
' największą liczbą
' dodatnią
d[i][j] = MAXINT
Next
' Jednakże na przekątnej
' wpisujemy 0
d[i][i] = 0
Next
For i = 1 To m
' Czytamy definicję krawędzi
Input #1, x, y, w
' Wagę krawędzi
' umieszczamy
' w macierzy d
d[x][y] = w
Next
Close #1
' Algorytm Floyda-Warshalla
For k = 0 To n-1
For i = 0 To n-1
For j = 0 To n-1
If (d[i][k] = MAXINT) OrElse _
(d[k][j] = MAXINT) Then _
Continue For
w = d[i][k]+d[k][j]
If d[i][j] > w Then _
d[i][j] = w
Next
Next
Next
' Wyświetlamy wyniki
Print
For i = 0 To n-1
For j = 0 To n-1
Print Using "d[&,&] =";i;j;
If d[i][j] = MAXINT Then
Print " NO PATH"
Else
Print d[i][j]
End If
Next
Next
' Usuwamy macierz dynamiczną
For i = 0 To n-1
Delete [] d[i]
Next
Delete [] d
End
|
Python
(dodatek)# Algorytm Floyda-Warshalla
# Data: 17.02.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------
# "plus nieskończoność"
MAXINT = 2147483647
# Czytamy liczbę
# wierzchołków
# oraz krawędzi
z = input().split()
n = int(z[0])
m = int(z[1])
# Tworzymy
# dynamiczną
# macierz d
d = [None] * n
for i in range(n):
# Tworzymy
# wiersz macierzy
d[i] = [MAXINT] * n
# Na przekątnej
# wpisujemy 0
d[i][i] = 0
for i in range(m):
# Czytamy definicję krawędzi
z = input().split()
x = int(z[0])
y = int(z[1])
w = int(z[2])
# Wagę krawędzi
# umieszczamy
# w macierzy d
d[x][y] = w
# Algorytm Floyda-Warshalla
for k in range(n):
for i in range(n):
for j in range(n):
if (d[i][k] == MAXINT) or \
(d[k][j] == MAXINT):
continue
w = d[i][k]+d[k][j]
if d[i][j] > w:
d[i][j] = w
# Wyświetlamy wyniki
print()
for i in range(n):
for j in range(n):
print("d[",i,",",j,"] = ",
sep="",end="")
if d[i][j] == MAXINT:
print("NO PATH")
else:
print(d[i][j])
# Usuwamy macierz dynamiczną
for i in range(n):
d[i] = None
del d
|
| Wynik: |
5 13 0 1 5 0 2 4 0 3 8 1 0 -4 1 2 -2 1 4 5 2 3 5 2 4 2 3 0 -1 3 1 2 3 4 -1 4 2 4 4 3 2 d[0,0] = 0 d[0,1] = 5 d[0,2] = 3 d[0,3] = 7 d[0,4] = 5 d[1,0] = -4 d[1,1] = 0 d[1,2] = -2 d[1,3] = 2 d[1,4] = 0 d[2,0] = 2 d[2,1] = 6 d[2,2] = 0 d[2,3] = 4 d[2,4] = 2 d[3,0] = -2 d[3,1] = 2 d[3,2] = 0 d[3,3] = 0 d[3,4] = -1 d[4,0] = 0 d[4,1] = 4 d[4,2] = 2 d[4,3] = 2 d[4,4] = 0 |
Podstawowy algorytm Floyda-Warshalla oblicza jedynie minimalne koszty ścieżek pomiędzy wszystkimi parami wierzchołków w grafie ważonym. Nie dostajemy jednakże informacji o samych ścieżkach. Przez prostą modyfikację algorytmu możemy również otrzymać te najkrótsze ścieżki. Najpierw jednak pokażemy, jak rozpoznać w grafie cykl ujemny. Otóż w tym celu wystarczy po zakończeniu działania algorytmu sprawdzić główną przekątną macierzy d. Jeśli jakikolwiek wyraz na głównej przekątnej będzie ujemny, to w grafie mamy cykl ujemny. Wyjaśnienie tego faktu jest proste. Koszt dojścia wierzchołka do siebie samego jest ustawiany na początku algorytmu na zero. Koszt każdego cyklu dodatniego jest większy od zera, zatem przejście przez cykl dodatni i powrót do wierzchołka ma koszt większy od zera i wartość ta nie będzie zmieniona. Jeśli jednak wierzchołek należy do cyklu ujemnego, to przejście przez ten cykl i powrót do wierzchołka daje wartość ujemną, którą zostanie zastąpione to zero. Jeśli graf zawiera cykl ujemny, to nie można w nim wyznaczyć wszystkich najkrótszych ścieżek (każda ścieżka mająca dostęp do takiego cyklu może stać się najkrótszą przez kilkakrotne przejście przez cykl ujemny). Dlatego po rozpoznaniu cyklu ujemnego algorytm powinien zgłosić błąd (np. jeśli realizujemy go w postaci funkcji, to zwraca true, gdy wszystko jest OK, a false, gdy napotka cykl ujemny).
Do zapamiętania ścieżek potrzebujemy dodatkowej macierzy poprzedników
p o rozmiarze
d[i,j] > d[i,k] + d[k,j]
i do macierzy d wprowadzamy:
d[i,j] ← d[i,k] + d[k,j]
to wierzchołek k-ty staje się wierzchołkiem pośrednim na najkrótszej
ścieżce łączącej wierzchołek i-ty z wierzchołkiem
j-tym. Z tego powodu jest on również poprzednikiem wierzchołka
p[i,j] ← p[k,j]
Wpis ten oznacza poprzednik wierzchołka
Odtworzenie poprawnej ścieżki wymaga dodatkowych działań. Po zakończeniu
pracy algorytmu Floyda-Warshala najkrótszą ścieżkę pomiędzy wierzchołkami
Rekurencyjnie szukamy ścieżki od wierzchołka
Prześledźmy działanie zmodyfikowanego algorytmu Floyda-Warshala w poniższej tabeli:
![]() |
|
|
Oto nasz graf ważony z ujemnymi wagami krawędzi. Przygotowujemy macierz kosztów dojścia d. Na głównej przekątnej umieszczamy 0, a w pozostałych komórkach umieszczamy ∞. Następnie przygotowujemy macierz poprzedników p. Wszystkie jej komórki wstępnie ustawiamy na -1. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Następnie dla każdej krawędzi u–v grafu w
komórce d[u,v] umieszczamy wagę krawędzi w (u–v). Wartość d[i,j] = ∞ oznacza, że wierzchołek i-ty nie łączy się krawędzią z wierzchołkiem j-tym (ale, jak zobaczymy dalej, może istnieć ścieżka łącząca te wierzchołki, a wtedy algorytm wprowadzi do d[i,j] jej koszt). Również modyfikujemy macierz p, umieszczając w p[u,v] wartość u. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Rozpoczynamy przeglądanie kolejnych wierzchołków grafu od wierzchołka k = 0. Dla każdej pary wierzchołków u, v sprawdzamy, czy zachodzi nierówność: d[u,v] > d[u,k] + d[k,v]. Jeśli tak, to d[u,v] ← d[u,k] + d[k,v]. W tym przypadku nierówność będzie spełniona dla par wierzchołków: (1,3): d[1,3] = ∞ > d[1,0]+d[0,3] = -4+8 = 4 (3,2): d[3,2] = ∞ > d[3,0]+d[0,2] = -1+4 = 3 Zmieniamy dla nich wpisy w macierzach d i p: d[1,3] ← 4, p[1,3] ← p[0,3] = 0 d[3,2] ← 3, p[3,2] ← p[0,2] = 0 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Idziemy do następnego wierzchołka grafu,
k = 1. Znów sprawdzamy podaną wyżej nierówność dla wszystkich par wierzchołków w grafie. Będzie spełniona dla par: (0,2): d[0,2] = 4 > d[0,1]+d[1,2] = 5+-2 = 3 (0,4): d[0,4] = ∞ > d[0,1]+d[1,4] = 5+ 5 = 10 (3,0): d[3,0] = -1 > d[3,1]+d[1,0] = 2+-4 = -2 (3,2): d[3,2] = 3 > d[3,1]+d[1,2] = 2+-2 = 0 Ustawiamy: d[0,2] ← 3, p[0,2] ← p[1,2] = 1 d[0,4] ← 10, p[0,4] ← p[1,4] = 1 d[3,0] ← -2, p[3,0] ← p[1,0] = 1 d[3,2] ← 0, p[3,2] ← p[3,2] = 1 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Następny wierzchołek, k = 2. Nierówność jest spełniona dla następujących par wierzchołków: (0,4): d[0,4] = 10 > d[0,2]+d[2,4] = 3+2 = 5 (1,3): d[1,3] = 4 > d[1,2]+d[2,3] = -2+5 = 3 (1,4): d[1,4] = 5 > d[1,2]+d[2,4] = -2+2 = 0 Ustawiamy: d[0,4] ← 5, p[0,4] ← p[2,4] = 2 d[1,3] ← 3, p[1,3] ← p[2,3] = 2 d[1,4] ← 0, p[1,4] ← p[1,4] = 2 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Następny wierzchołek, k = 3. (2,0): d[2,0] = ∞ > d[2,3]+d[3,0] = 5+-2 = 3 (2,1): d[2,1] = ∞ > d[2,3]+d[3,1] = 5+ 2 = 7 (4,0): d[4,0] = ∞ > d[4,3]+d[3,0] = 2+-2 = 0 (4,1): d[4,1] = ∞ > d[4,3]+d[3,1] = 2+ 2 = 4 (4,2): d[4,2] = 4 > d[4,3]+d[3,2] = 2+ 0 = 2 Ustawiamy: d[2,0] ← 3, p[2,0] ← p[3,0] = 1 d[2,1] ← 7, p[2,1] ← p[3,1] = 3 d[4,0] ← 0, p[4,0] ← p[3,0] = 1 d[4,1] ← 4, p[4,1] ← p[3,1] = 3 d[4,2] ← 2, p[4,2] ← p[3,2] = 1 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
|
Ostatni wierzchołek, k = 4. (0,3): d[0,3] = 8 > d[0,4]+d[4,3] = 5+2 = 7 (1,3): d[1,3] = 3 > d[1,4]+d[4,3] = 0+2 = 2 (2,0): d[2,0] = 3 > d[2,4]+d[4,0] = 2+0 = 2 (2,1): d[2,1] = 7 > d[2,4]+d[4,1] = 2+4 = 6 (2,3): d[2,3] = 5 > d[2,4]+d[4,3] = 2+2 = 4 Ustawiamy: d[0,3] ← 7, p[0,3] ← p[4,3] = 4 d[1,3] ← 2, p[1,3] ← p[4,3] = 4 d[2,0] ← 2, p[2,0] ← p[4,0] = 1 d[2,1] ← 6, p[2,1] ← p[4,1] = 3 d[2,3] ← 4, p[2,3] ← p[4,3] = 4 |
Teraz pokażemy, jak wydobyć rekurencyjnie
![]() |
|
Poziom rekurencji 0: i = 4, j = 2 Ponieważ i nie jest równe j oraz p[4,2] jest różne od -1, to wywołujemy rekurencyjnie ten sam algorytm z parametrami: i = 4, j = p[4,2] = 1. Powrót nastąpi w to miejsce, gdzie wypiszemy j i zakończymy algorytm. |
||||||||||||||||||||||||||||||||||||
![]() |
|
Poziom rekurencji 1: i = 4, j = 1 Wywołujemy rekurencyjnie ten sam algorytm z parametrami: i = 4, j = p[4,1] = 3. |
||||||||||||||||||||||||||||||||||||
![]() |
|
Poziom rekurencji 2: i = 4, j = 3 Wywołujemy rekurencyjnie ten sam algorytm z parametrami: i = 4, j = p[4,3] = 4. |
||||||||||||||||||||||||||||||||||||
![]() |
|
Poziom rekurencji 3: i = 4, j = 4 Zachodzi równość i = j. Wypisujemy 4 i wracamy na poziom 2. |
||||||||||||||||||||||||||||||||||||
![]() |
Wy: 4 3 | Poziom rekurencji 2: i = 4, j = 3 Po powrocie z wywołania rekurencyjnego na poziomie 3 wypisujemy j i wracamy na poziom 1. |
||||||||||||||||||||||||||||||||||||
![]() |
Wy: 4 3 1 | Poziom rekurencji 1: i = 4, j = 1 Po powrocie z wywołania rekurencyjnego na poziomie 2 wypisujemy j i wracamy na poziom 0. |
||||||||||||||||||||||||||||||||||||
![]() |
Wy: 4 3 1 2 | Poziom rekurencji 0: i = 4, j = 2 Po powrocie z wywołania rekurencyjnego na poziomie 1 wypisujemy j i kończymy algorytm. Ścieżka jest gotowa. |
true – poprawne zakończenie, wynikiem są zawartości dwóch macierzy:
d : n × n elementowa macierz kosztów dojścia. Elementfalse – graf zawiera cykl ujemny.
K01: Utwórz macierze d i p o rozmiarze n×n K02: Dla i = 0, 1, …, n-1: wykonuj kroki K03…K05 K03: Dla j = 0, 1, …, n-1, ; ustawiamy wszystkie komórki d wykonuj: ; na nieskończoność, a wszystkie komórki p na -1 d[i,j] ← ∞ p[i,j] ← -1 K04: d[i,i] ← 0 ; elementy głównej przekątnej zerujemy K05: Dla każdego sąsiada j wierzchołka i, ; ustawiamy w d wagi wykonuj: ; krawędzi, a w p poprzedniki wierzchołków j-tych d[i,j] ← waga krawędzi i–j p[i,j] ← i K06: Dla k = 0, 1, …, n-1, ; przechodzimy przez wszystkie wykonuj kroki K07…K09 ; wierzchołki grafu K07: Dla i = 0, 1, …, n-1, ; wybieramy wszystkie pary wykonuj kroki K08…K09 ; i, j wierzchołków w grafie K08: Dla j = 0, 1, …, n-1, ; wykonuj krok K09 K09: Jeśli d[i,j] > d[i,k]+d[k,j], ; testujemy nierówność i, to: ; jeśli jest spełniona, modyfikujemy koszt. d[i,j] ← d[i,k]+d[k,j] ; Jako poprzednik j na ścieżce od i do j p[i,j] ← p[k,j] ; ustawiamy poprzednik j na ścieżce od k do j. K10: Dla i = 0, 1, …, n-1: wykonuj: Jeśli d[i,i] = -1, ; wykryto cykl ujemny to zakończ z wynikiem false K11: Zakończ z wynikiem true
minimalna ścieżka od wierzchołka
K01: Jeśli i = j, to pisz i i zakończ K02: Jeśli p[i,j] = -1, to pisz "NO PATH" i zakończ K03: FWPath(p,i,p[i,j]) ; wywołanie rekurencyjne K04: Pisz j ; ostatni wierzchołek ścieżki K05: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Poniższy wyznacza ścieżki o najniższym koszcie dojścia dla wszystkich par wierzchołków w grafie
za pomocą rozszerzonego algorytmu Floyda-Warshalla. Definicja danych
wejściowych jest następująca: W pierwszym wierszu program odczytuje kolejno liczbę wierzchołków n oraz liczbę krawędzi m. W m kolejnych wierszach znajdują się definicje krawędzi. Definicja składa się z trzech liczb x y w. Pierwsza liczba x oznacza początek krawędzi, druga liczba y oznacza koniec krawędzi, a trzecia liczba w jest wagą tej krawędzi. Na wyjściu dla każdej pary wierzchołków program wypisuje minimalną ścieżkę oraz jej koszt. Jeśli ścieżka nie istnieje, pojawia się napis "NO PATH". |
![]() |
5 13 0 1 5 0 2 4 0 3 8 1 0 -4 1 2 -2 1 4 5 2 3 5 2 4 2 3 0 -1 3 1 2 3 4 -1 4 2 4 4 3 2 |
Pascal// Algorytm Floyda-Warshalla
// Data: 21.04.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program floyd_warshall2;
// Typ danych dla
// macierzy dynamicznej
type
nptr = array of integer;
// Zmienne globalne
var
// Macierze kosztów
// oraz poprzedników
d, p : array of nptr;
// Liczba wierzchołków
// i krawędzi
n, m : integer;
// Funkcja wyznaczania
// kosztów dojścia oraz
// minimalnych ścieżek
// w grafie ważonym
//---------------------
function FloydWarshall : boolean;
var
i, j, k, w : integer;
begin
for k := 0 to n-1 do
for i := 0 to n-1 do
for j := 0 to n-1 do
begin
if (d[i][k] = MAXINT) or
(d[k][j] = MAXINT) then
continue;
w := d[i][k] + d[k][j];
if d[i][j] > w then
begin
d[i][j] := w;
p[i][j] := p[k][j];
end;
end;
for i := 0 to n-1 do
if d[i][i] < 0 then
// Ujemny cykl
Exit(false);
FloydWarshall := true;
end;
// Rekurencyjna procedura
// odtwarzania minimalnej
// ścieżki z macierzy
// poprzedników p
//-----------------------
procedure FWPath(i, j : integer);
begin
if i = j then
write(i, ' ')
else if p[i][j] = -1 then
write('NO PATH')
else
begin
FWPath(i, p[i][j]);
write (j, ' ');
end;
end;
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
var
i, j, x, y, w : integer;
begin
// Czytamy liczbę
// wierzchołków oraz
// krawędzi
read(n, m);
// Tworzymy dynamiczną
// macierz d
SetLength(d, n);
// Tworzymy dynamiczną
// macierz p
SetLength(p, n);
for i := 0 to n-1 do
begin
// Tworzymy wiersz
// macierzy d
SetLength(d[i], n);
// Tworzymy wiersz
// macierzy p
SetLength(p[i], n);
for j := 0 to n-1 do
begin
// Wiersz d wypełniamy
// największą liczbą
// dodatnią
d[i][j] := MAXINT;
// Wiersz p wypełniamy
// liczbami -1
// (brak poprzednika)
p[i][j] := -1;
end;
// Jednakże na przekątnej d
// wpisujemy 0
d[i][i] := 0;
end;
for i := 1 to m do
begin
// Czytamy definicję
// krawędzi
read(x, y, w);
// Wagę krawędzi
// umieszczamy
// w macierzy d
d[x][y] := w;
// Poprzednikiem y jest x
p[x][y] := x;
end;
// Wywołujemy funkcję
// Floyda-Warshalla
writeln;
if FloydWarshall then
begin
// Wyświetlamy wyniki
for i := 0 to n-1 do
for j := 0 to n-1 do
begin
write(i, '-', j, ' : ');
FWPath(i, j);
if d[i][j] < MAXINT then
write('$', d[i][j]);
writeln;
end;
end
else
writeln('Negative cycle found');
writeln;
// Usuwamy macierze dynamiczne
for i := 0 to n-1 do
begin
SetLength(d[i], 0);
SetLength(p[i], 0);
end;
SetLength(d, 0);
SetLength(p, 0);
end.
|
// Algorytm Floyda-Warshalla
// Data: 21.04.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
using namespace std;
// "plus nieskończoność"
const int MAXINT = 2147483647;
// Zmienne globalne
//-----------------
// Macierze kosztów
// oraz poprzedników
int **d, **p;
// Liczba wierzchołków
// i krawędzi
int n, m;
// Funkcja wyznaczania
// kosztów dojścia oraz
// minimalnych ścieżek
// w grafie ważonym
//---------------------
bool FloydWarshall()
{
int i, j, k, w;
for(k = 0; k < n; k++)
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
if((d[i][k] == MAXINT) ||
(d[k][j] == MAXINT))
continue;
w = d[i][k]+d[k][j];
if(d[i][j] > w)
{
d[i][j] = w;
p[i][j] = p[k][j];
}
}
for(i = 0; i < n; i++)
if(d[i][i] < 0) return
// Ujemny cykl
false;
return true;
}
// Rekurencyjna procedura
// odtwarzania minimalnej
// ścieżki z macierzy
// poprzedników p
//-----------------------
void FWPath(int i, int j)
{
if(i == j)
cout << i << " ";
else if(p[i][j] == -1)
cout << "NO PATH";
else
{
FWPath (i, p [i][j]);
cout << j << " ";
}
}
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
int main()
{
int i, j, x, y, w;
// Czytamy liczbę
// wierzchołków oraz
// krawędzi
cin >> n >> m;
// Tworzymy dynamiczną
// macierz d
d = new int * [n];
// Tworzymy dynamiczną
// macierz p
p = new int * [n];
for(i = 0; i < n; i++)
{
// Tworzymy wiersz
// macierzy d
d[i] = new int [n];
// Tworzymy wiersz
// macierzy p
p[i] = new int [n];
for(j = 0; j < n; j++)
{
// Wiersz d wypełniamy
// największą liczbą
// dodatnią
d[i][j] = MAXINT;
// Wiersz p wypełniamy
// liczbami -1
// (brak poprzednika)
p[i][j] = -1;
}
// Jednakże na przekątnej d
// wpisujemy 0
d[i][i] = 0;
}
for(i = 0; i < m; i++)
{
// Czytamy definicję krawędzi
cin >> x >> y >> w;
// Wagę krawędzi umieszczamy
// w macierzy d
d[x][y] = w;
// Poprzednikiem y jest x
p[x][y] = x;
}
// Wywołujemy procedurę
// Floyda-Warshalla
cout << endl;
if(FloydWarshall())
{
// Wyświetlamy wyniki
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
cout << i << "-"
<< j << ": ";
FWPath(i, j);
if(d[i][j] < MAXINT)
cout << "$" << d[i][j];
cout << endl;
}
}
else
cout << "Negative cycle found"
<< endl;
cout << endl;
// Usuwamy macierze dynamiczne
for(i = 0; i < n; i++)
{
delete [] d[i];
delete [] p[i];
}
delete [] d;
delete [] p;
return 0;
}
|
Basic' Algorytm Floyda-Warshalla
' Data: 21.04.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
' "plus nieskończoność"
const MAXINT = 2147483647
' Zmienne globalne
'-----------------
' Macierze kosztów oraz poprzedników
Dim Shared As Integer Ptr Ptr d, p
' Liczba wierzchołków i krawędzi
Dim Shared As Integer n, m
' Funkcja wyznaczania
' kosztów dojścia oraz
' minimalnych ścieżek
' w grafie ważonym
'---------------------
Function FloydWarshall As Integer
Dim As Integer i, j, k, w
For k = 0 To n-1
For i = 0 To n-1
For j = 0 To n-1
if (d[i][k] = MAXINT) OrElse _
(d[k][j] = MAXINT) Then _
Continue For
w = d[i][k]+d[k][j]
If d[i][j] > w Then
d[i][j] = w
p[i][j] = p[k][j]
End If
Next
Next
Next
For i = 0 To n-1
If d[i][i] < 0 Then
' Ujemny cykl
Return 0
End If
Next
Return 1
End Function
' Rekurencyjna procedura
' odtwarzania minimalnej
' ścieżki z macierzy
' poprzedników p
'----------------------
Sub FWPath(ByVal i As Integer, _
ByVal j As Integer)
If i = j Then
Print i;
ElseIf p[i][j] = -1 Then
Print " NO PATH";
Else
FWPath(i, p[i][j])
Print j;
End If
End Sub
' **********************
' *** PROGRAM GŁÓWNY ***
' **********************
Dim As Integer i, j, x, y, w
Open Cons For Input As #1
' Czytamy liczbę wierzchołków
' oraz krawędzi
Input #1, n, m
' Tworzymy dynamiczną
' macierz d
d = New Integer Ptr [n]
' Tworzymy dynamiczną
' macierz p
p = New Integer Ptr [n]
For i = 0 To n-1
' Tworzymy wiersz
' macierzy d
d[i] = New Integer [n]
' Tworzymy wiersz
' macierzy p
p[i] = New Integer [n]
For j = 0 To n-1
' Wiersz d wypełniamy
' największą liczbą
' dodatnią
d[i][j] = MAXINT
' Wiersz p wypełniamy
' liczbami -1
' (brak poprzednika)
p[i][j] = -1
Next
d[i][i] = 0
Next
For i = 1 To m
' Czytamy definicję krawędzi
Input #1, x, y, w
' Wagę krawędzi umieszczamy
' w macierzy d
d[x][y] = w
' Poprzednikiem y jest x
p[x][y] = x
Next
' Wywołujemy funkcję
' Floyda-Warshalla
Print
If FloydWarshall() = 1 Then
' Wyświetlamy wyniki
For i = 0 To n-1
For j = 0 To n-1
Print Using "&-&:";i;j;
FWPath(i, j)
If d[i][j] < MAXINT Then _
Print Using " $&";d[i][j];
Print
Next
Next
Else
Print "Negative cycle found"
End If
Print
' Usuwamy macierze dynamiczne
for i = 0 To n-1
Delete [] d[i]
Delete [] p[i]
Next
Delete [] d
Delete [] p
End
|
Python
(dodatek)# Algorytm Floyda-Warshalla
# Data: 19.02.2025
# (C)2014 mgr Jerzy Wałaszek
#---------------------------
# "plus nieskończoność"
MAXINT = 2147483647
# Funkcja wyznaczania
# kosztów dojścia oraz
# minimalnych ścieżek
# w grafie ważonym
#---------------------
def floyd_warshall():
global n,d,p
for k in range(n):
for i in range(n):
for j in range(n):
if (d[i][k] == MAXINT) or \
(d[k][j] == MAXINT):
continue
w = d[i][k]+d[k][j]
if d[i][j] > w:
d[i][j] = w
p[i][j] = p[k][j]
for i in range(n):
if d[i][i] < 0:
# Ujemny cykl
return False
return True
# Rekurencyjna procedura
# odtwarzania minimalnej
# ścieżki z macierzy
# poprzedników p
#----------------------
def fw_path(i, j):
global p
if i == j:
print(i, end=" ")
elif p[i][j] == -1:
print(" NO PATH", end=" ")
else:
fw_path(i, p[i][j])
print(j, end=" ")
# **********************
# *** PROGRAM GŁÓWNY ***
# **********************
# Czytamy liczbę wierzchołków
# oraz krawędzi
z = input().split()
n = int(z[0])
m = int(z[1])
# Tworzymy dynamiczną
# macierz d
d = [None] * n
# Tworzymy dynamiczną
# macierz p
p = [None] * n
for i in range(n):
# Tworzymy wiersz
# macierzy d
d[i] = [MAXINT] * n
# Tworzymy wiersz
# macierzy p
p[i] = [-1] * n
d[i][i] = 0
for i in range(m):
# Czytamy definicję krawędzi
z = input().split()
x = int(z[0])
y = int(z[1])
w = int(z[2])
# Wagę krawędzi umieszczamy
# w macierzy d
d[x][y] = w
# Poprzednikiem y jest x
p[x][y] = x
# Wywołujemy funkcję
# Floyda-Warshalla
print()
if floyd_warshall():
# Wyświetlamy wyniki
for i in range(n):
for j in range(n):
print(i,"-",j,sep="",end=" ")
fw_path(i, j)
if d[i][j] < MAXINT:
print(" $",d[i][j],sep="",end=" ")
print()
else:
print("Negative cycle found")
print()
# Usuwamy macierze dynamiczne
for i in range(n):
d[i] = None
p[i] = None
del d,p
|
| Wynik: |
5 13 0 1 5 0 2 4 0 3 8 1 0 -4 1 2 -2 1 4 5 2 3 5 2 4 2 3 0 -1 3 1 2 3 4 -1 4 2 4 4 3 2 0-0 : 0 $0 0-1 : 0 1 $5 0-2 : 0 1 2 $3 0-3 : 0 1 2 4 3 $7 0-4 : 0 1 2 4 $5 1-0 : 1 0 $-4 1-1 : 1 $0 1-2 : 1 2 $-2 1-3 : 1 2 4 3 $2 1-4 : 1 2 4 $0 2-0 : 2 4 3 1 0 $2 2-1 : 2 4 3 1 $6 2-2 : 2 $0 2-3 : 2 4 3 $4 2-4 : 2 4 $2 3-0 : 3 1 0 $-2 3-1 : 3 1 $2 3-2 : 3 1 2 $0 3-3 : 3 $0 3-4 : 3 4 $-1 4-0 : 4 3 1 0 $0 4-1 : 4 3 1 $4 4-2 : 4 3 1 2 $2 4-3 : 4 3 $2 4-4 : 4 $0 |
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.