|
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
|
ProblemDla danej sieci przepływowej należy określić jej maksymalny przepływ. |
Naturalnym problemem wyłaniającym się w sieciach przepływowych jest problem maksymalnego przepływu (ang. maximum flow problem). W problemie tym musimy określić maksymalną wielkość przepływu ze źródła do ujścia sieci przy ograniczeniach przepustowości nałożonych na poszczególne kanały.
Problem wyznaczania maksymalnego przepływu rozwiązuje algorytm Forda-Fulkersona. Opiera się on na idei sieci rezydualnych (ang. residual network) oraz ścieżek rozszerzających (ang. augmenting path). Sieć rezydualna powstaje z sieci przepływowej przez zastąpienie przepustowości kanałów przepustowościami rezydualnymi wyliczanymi wg poniższego wzoru:
cf(u,v) = c(u,v)-f(u,v)
Gdzie:
cf : przepustowość rezydualna (ang. residual capacity).Ponieważ w odwrotnym kierunku również występuje przepływ (ujemny), w sieci rezydualnej mogą pojawiać się dodatkowe kanały skierowane przeciwnie do kanałów pierwotnych. Dla kanałów przeciwnych przepustowość rezydualna wyraża się wzorem:
cf(v,u) = f(u,v)
czyli jest równa przepływowi w kanale pierwotnym. Uzasadnienie jest bardzo
proste – ponieważ w sieci pierwotnej nie istnieje kanał łączący wierzchołki
v i u (istnieje tylko kanał od u do v),
to jego przepustowość c(v,u) jest równa
0, co wynika bezpośrednio z definicji przepustowości. Z kolei z własności
skośnej symetryczności przepływu otrzymujemy, iż
cf(v,u) = c(v,u)–f(v,u) = 0–(-f(u,v)) = f(u,v)
Ścieżka rozszerzająca jest ścieżką w sieci rezydualnej (to ważne – w pierwotnej sieci takiej ścieżki może nie być!!!) łączącą źródło s z ujściem t. Wszystkie kanały leżące na ścieżce rozszerzającej muszą posiadać niezerowe przepustowości rezydualne. Przepustowość rezydualna ścieżki rozszerzającej jest równa najmniejszej przepustowości rezydualnej kanałów należących do tej ścieżki. Fakt ten zapisujemy następująco:
cf(p) = min{cf(u,v) : (u v) ∈ p}
Warunek ten jest bardzo łatwy do zrozumienia – ścieżka rozszerzająca służy do powiększania przepływu wzdłuż zawartych w niej kanałów. Powiększony przepływ będzie przepływał przez każdy z kanałów na ścieżce, dlatego można go powiększyć tylko o tyle, ile wynosi najmniejsza przepustowość rezydualna kanałów ścieżki.
Podstawowy algorytm Forda-Fulkersona brzmi następująco:
Wyzeruj wszystkie przepływy w sieciDopóki w sieci rezydualnej istnieje
ścieżka rozszerzająca p, zwiększaj przepływ
Aby lepiej zrozumieć ten algorytm, oprzyjmy się na prostym przykładzie.
![]() |
Oto nasza sieć przepływowa. W kanałach zaznaczyliśmy ich przepustowości. Przepływy netto są zerowe. Również przepływ sieci |f| = 0. |
![]() |
Dla zerowych przepływów sieć rezydualna jest identyczna z siecią pierwotną. Szukamy w niej ścieżki rozszerzającej, która połączy źródło s z ujściem t. Takich ścieżek może być wiele. Umówmy się, że wybieramy najkrótszą z nich (mającą najmniej krawędzi). Na przykład może to być ścieżka: p = {s→A→B→t} Na ścieżce p znajdują się trzy kanały sieci rezydualnej: (s,A), (A,B) i (B,t). Przepustowość rezydualna cf(p) ścieżki jest równa najmniejszej przepustowości rezydualnej jej kanałów. Najmniejszą przepustowość rezydualną posiada kanał (B,t), dla którego cf(B,t) = 6. Zatem wzdłuż krawędzi ścieżki przepływ można zwiększyć o 6 jednostek. O tyle rośnie również przepływ sieciowy, czyli |fnowy| = |fstary| + cf(p) = 0+6 = 6 |
![]() |
Zwiększenie przepływu w kanale sieci pierwotnej o cf(p) odpowiada zmniejszeniu przepustowości rezydualnej tego kanału. Jednocześnie wraz z pojawieniem się przepływu w kanale sieci pierwotnej powstaje kanał przeciwny w siec rezydualnej o przepustowości rezydualnej równej przepływowi. Przepustowość rezydualna kanału (s,A) wynosi 3 – oznacza to, iż kanałem tym można wciąż jeszcze przesłać trzy dodatkowe jednostki przepływu. Zwróć uwagę, iż w sieci rezydualnej pojawił się kanał przeciwny (A,s) o przepustowości rezydualnej cf(A,s) = 6. Kanał (A,B) może jeszcze przesłać 1 dodatkową jednostkę przepływu. Również tutaj pojawił się kanał przeciwny o przepustowości rezydualnej równej 6. Kanał (B,t) przestał istnieć w sieci rezydualnej, ponieważ osiągnął już swoją maksymalną przepustowość – 6 jednostek przepływu. Nie może on być dalej wykorzystywany do powiększania przepływu. Na jego miejscu mamy jednak kanał przeciwny z przepustowością rezydualną równą 6. |
![]() |
W nowej sieci rezydualnej szukamy kolejnej ścieżki rozszerzającej: p = {s→A→C→t}, cf(p) = 3 |
![]() |
Przepływ zwiększamy: |f| = 6+3 = 9 i modyfikujemy przepustowości rezydualne krawędzi ścieżki rozszerzającej otrzymując nową sieć rezydualną. Znikają z niej kanały (s,A) i (A,C) – wykorzystały już swój potencjał zwiększania przepływu. |
![]() |
Szukamy kolejnej ścieżki rozszerzającej: p = {s→D→E→t}, cf(p) = 6 |
![]() |
W nowej sieci rezydualnej zniknął kanał (D,E). |
![]() |
Wciąż jednakże możemy znaleźć nową ścieżkę rozszerzającą: p = {s→D→C→t }, cf(p) = 3 |
![]() |
Przepływ zwiększamy: |f| = 15+3 = 18. Po zmodyfikowaniu sieci rezydualnej otrzymujemy nową sieć rezydualną. W tej sieci rezydualnej nie znajdziemy już żadnej nowej ścieżki rozszerzającej – ze źródła s nie wychodzi żaden kanał. |
![]() |
Otrzymaliśmy maksymalny przepływ. Z sieci rezydualnej można w prosty sposób przejść do sieci przepływowej wraz z rozkładem przepływów na poszczególne kanały. Wystarczy od przepustowości kanałów odjąć otrzymane przepustowości rezydualne – dla nieistniejących kanałów ich przepustowość rezydualna wynosi 0. W efekcie otrzymamy sieć przepływową z wyznaczonym maksymalnym przepływem sieciowym. Zwróć uwagę, iż poprzez kanały (B,C) i (C,E) przepływ nie płynie. Są to kanały zbędne, które można zlikwidować. |
Jeśli sieć przepływowa nie posiada kanałów skierowanych przeciwnie
f(D,E) = cf(E,D) = 6
a przez kanał
f(C,t) = cf(t,C) = 6.
Algorytm Forda-Fulkersona jest właściwie szablonem postępowania. Aby go efektywnie zaimplementować, programista musi rozwiązać kilka problemów. Pierwszym z nich jest wybór sposobu reprezentowania sieci rezydualnej. Zasadniczo mamy dwie możliwości:
Drugim problemem w algorytmie Forda-Fulkersona jest sposób wyszukiwania ścieżek rozszerzających w sieci rezydualnej. Okazuje się, iż zastosowanie prostej metody DFS (ang. Deep First Search – przeszukiwanie w głąb) nie daje dobrych rezultatów, ponieważ DFS zwykle znajduje długie ścieżki. Tymczasem w algorytmie Forda-Fulkersona są preferowane ścieżki krótkie, czyli zawierające jak najmniejszą liczbę krawędzi grafu sieci przepływowej. Rozwiązaniem staje się zastosowanie metody BFS (ang. Breadth First Search – przeszukiwanie w szerz), która zawsze znajduje najkrótsze ścieżki ze względu na ilość krawędzi.
Algorytm Forda-Fulkersona wykorzystujący metodę BFS do wyszukiwania ścieżek rozszerzających w sieci rezydualnej nosi nazwę algorytmu Edmondsa-Karpa.
n : liczba wierzchołków w grafie;
n ∈ C.
C : macierz n ×
n przepustowości kanałów.
s : numer wierzchołka będącego źródłem sieci;
s ∈ C.
t : numer wierzchołka będącego ujściem sieci;
t ∈ C.
F : macierz
n × n przepływów
netto.
fmax : wartość maksymalnego przepływu sieciowego.
Q : kolejka przechowująca wierzchołki dla metody BFS.
P : tablica n elementowa przechowująca ścieżki
(poprzedniki) wyszukiwane przez BFS.
CFP : tablica n elementowa przechowująca wartości
cf(p) dla ścieżki kończącej się w danym węźle sieci.
cp : przechowuje wartość przepustowości rezydualnej.
x,y : przechowują numery wierzchołków połączonych
krawędzią; x,y ∈ C.
K01: fmax ← 0 ; zerujemy wartość przepływu sieciowego K02: Wyzeruj macierz F K03: Dla y = 0, 1, …, n-1: wykonuj: P[y] ← -1 K04: P[s] ← -2 ; to zapobiegnie wybieraniu źródła przez BFS K05: CFP[s] ← ∞ ; w CFP przechowujemy przepustowość rezydualną ścieżki K06: Dopóki Q.empty() = false, wykonuj Q.pop() ; zerujemy kolejkę K07: Q.push(s) ; w kolejce umieszczamy numer źródła K08: Dopóki Q.empty() = false, wykonuj kroki K09…K16 K09: x = Q.front(); Q.pop() ; pobieramy numer wierzchołka z kolejki K10: Dla y = 0, 1, …, n-1: ; rozpoczynamy BFS wykonuj kroki K11…K16 K11: cp ← C[x][y]-F[x][y] ; wyznaczamy przepustowość rezydualną kanału (x,y) K12: Jeśli (cp = 0)(P[y] ≠ -1), ; omijamy przy braku krawędzi to następny obieg pętli K10 ; lub odwiedzonym sąsiedzie K13: P[y] ← x ; zapamiętujemy poprzednik na ścieżce K14: CPF[y] ← min(CPF[x],cp) ; obliczamy przepustowość rezydualną do węzła y K15: Jeśli y = t, ; ścieżka rozszerzająca znaleziona! to idź do kroku K18 K16: Q.push(y) K17: Zakończ ; brak ścieżki, kończymy K18: fmax ← fmax + CFP[t] ; zwiększamy przepływ sieciowy K19: Dopóki y ≠ s, ; cofamy się po ścieżce rozszerzającej od ujścia t do źródła s wykonuj kroki K20…K23 K20: x ← P[y] ; (x, y) jest krawędzią ścieżki rozszerzającej K21: F[x,y] ← F[x,y]+CFP[t] ; w kierunku zgodnym ze ścieżką zwiększamy przepływ K22: F[y,x] ← F[y,x]-CFP[t] ; w kierunku przeciwnym zmniejszamy przepływ K23: y ← x ; przechodzimy do następnej krawędzi ścieżki K24: Idź do kroku K03
|
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. |
| Program odczytuje definicję sieci
przepływowej o następującej postaci: Pierwsze dwie liczby określają kolejno liczbę wierzchołków n oraz liczbę krawędzi m sieci przepływowej. Dalej występuje m trójek liczb określających krawędzie sieci. Każda trójka definiuje kolejno: wierzchołek startowy, wierzchołek końcowy oraz przepustowość kanału. W naszym przykładzie przepustowości są liczbami całkowitymi. Na samym końcu danych występują dodatkowo dwie liczby, które definiują źródło i ujście sieci. Po odczytaniu definicji sieci program wyznacza maksymalny przepływ oraz przepływy netto w poszczególnych kanałach sieci. |
![]() |
7 11 0 1 7 0 3 3 1 3 4 1 4 6 2 0 9 2 5 9 3 4 9 3 6 2 5 3 3 5 6 6 6 4 8 2 4 |
Pascal// Maksymalny przepływ
// algorytm Edmondsa-Karpa
// Data: 08.08.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program maxflow;
// Definicja typu
// elementów listy
//----------------
type
PSLel = ^SLel;
SLel = record
next : PSLel;
Data: integer;
end;
// Definicja typu
// obiektowego queue
//------------------
queue = object
private
head : PSLel;
tail : PSLel;
public
constructor init;
destructor destroy;
function empty : boolean;
function front : integer;
procedure push(v : integer);
procedure pop;
end;
//---------------------
// Metody obiektu queue
//---------------------
// Konstruktor
//------------
constructor queue.init;
begin
head := nil;
tail := nil;
end;
// Destruktor
//-----------
destructor queue.destroy;
begin
while not empty do pop;
end;
// Sprawdza,
// czy kolejka jest pusta
//-----------------------
function queue.empty : boolean;
begin
if head = nil then
empty := true
else
empty := false;
end;
// Zwraca początek kolejki.
// Wartość specjalna to -MAXINT
//-----------------------------
function queue.front : integer;
begin
if head = nil then
front := -MAXINT
else
front := head^.data;
end;
// Zapisuje do kolejki
//--------------------
procedure queue.push(v : integer);
var
p : PSLel;
begin
new(p);
p^.next := nil;
p^.Data:= v;
if tail = nil then
head := p
else
tail^.next := p;
tail := p;
end;
// Usuwa z kolejki
//----------------
procedure queue.pop;
var
p : PSLel;
begin
if head <> nil then
begin
p := head;
head := head^.next;
if head = nil then
tail := nil;
dispose(p);
end;
end;
//---------------
// Program główny
//---------------
var
// Kolejka
Q : queue;
// Macierz przepustowości
C : array of array of integer;
// Macierz przepływów netto
F : array of array of integer;
// Tablica poprzedników
P : array of integer;
// Tablica przepustowości
// rezydualnych
CFP : array of integer;
// Zmienne proste algorytmu
n,m,s,t,fmax,cp,x,y,i,j : integer;
// Do wychodzenia
// z zagnieżdżonych pętli
esc : boolean;
begin
// Inicjujemy kolejkę
Q.init;
// Ze standardowego wejścia
// odczytujemy:
// n - liczbę wierzchołków
// w grafie sieci
// m - liczbę krawędzi
read(n, m);
// Tworzymy dynamicznie macierze:
// C - przepustowości krawędzi
// F - przepływy w krawędziach
SetLength(C, n);
SetLength(F, n);
for i := 0 to n-1 do
begin
SetLength(C[i], n);
SetLength(F[i], n);
end;
// Tworzymy dynamicznie tablice:
// P - poprzedniki na ścieżkach
// tworzonych przez BFS
// CFP - przepustowość ścieżek
SetLength(P, n);
SetLength(CFP, n);
// Zerujemy macierze
// przepustowości i przepływów
for i := 0 to n-1 do
for j := 0 to n-1 do
begin
C[i][j] := 0;
F[i][j] := 0;
end;
// Ze standardowego wejścia
// odczytujemy definicje krawędzi.
// Każda definicja składa się
// z trzech liczb:
// x,y - wierzchołki grafu
// połączone krawędzią
// cp - przepustowość krawędzi
// Odczytane dane zapamiętujemy:
// C[x][y] = cp
for i := 1 to m do
begin
read(x, y, cp);
C[x][y] := cp;
end;
// Na koniec odczytujemy
// numer wierzchołka źródła s
// oraz numer wierzchołka
// ujścia t
read(s, t);
//*****************************
//** Tutaj rozpoczyna się **
//** algorytm Edmondsa-Karpa **
//*****************************
// Rozpoczynamy od maksymalnego
// przepływu równego zero
fmax := 0;
// W pętli szukamy ścieżek
// rozszerzających dotąd,
// dopóki istnieją w sieci
// rezydualnej. Każda znaleziona
// ścieżka zwiększa przepływ
// wzdłuż zawartych w niej
// krawędzi grafu sieci
// przepływowej
while true do
begin
// Na początku pętli
// zerujemy tablicę
// poprzedników P
for i := 0 to n-1 do
P[i] := -1;
// źródło nie posiada
// poprzednika. Wpisujemy
// tutaj -2, aby BFS nie
// wybierało źródła
P[s] := -2;
// Do CFP[s] wpisujemy
// największą liczbę
// całkowitą
CFP[s] := MAXINT;
// Zerujemy kolejkę
// i umieszczamy w niej
// źródło s
while not Q.empty do
Q.pop;
Q.push(s);
// Zmienna esc umożliwia
// odpowiednie wychodzenie
// z dwóch zagnieżdżonych
// pętli - zamiast polecenia
// goto.
esc := false;
// Rozpoczynamy pętlę
// wyszukującą ścieżki BFS.
// Pętla kończy się
// w przypadku braku
// dalszych wierzchołków
// w kolejce
while not Q.empty do
begin
// Z początku kolejki
// pobieramy element
// i usuwamy go z kolejki
x := Q.front; Q.pop;
// Sprawdzamy wszystkich
// sąsiadów wierzchołka x
// przeglądając wiersz
// macierzy C
for y := 0 to n-1 do
begin
// Dla sąsiada y
// wyznaczamy przepustowość
// rezydualną krawędzi x->y.
// Jeśli krawędź nie
// istnieje w sieci,
// to otrzymamy w cp wynik
// zero
cp := C[x][y]-F[x][y];
// Sprawdzamy, czy krawędź
// istnieje oraz czy
// wierzchołek y nie był
// już wcześniej wybrany
// przez BFS. W takim
// przypadku P[y] ma wartość
// różną od -1.
if (cp <> 0) and
(P[y] = -1) then
begin
// W P[y] zapamiętujemy,
// iż poprzednikiem y
// jest x
P[y] := x;
// Dla wierzchołka y
// obliczamy dotychczasową
// przepustowość rezydualną
// ścieżki. Jest to mniejsza
// wartość z przepustowości
// ścieżki dla poprzednika x
// i bieżącej przepustowości
// rezydualnej krawędzi
// x->y.
if CFP[x] > cp then
CFP[y] := cp
else
CFP[y] := CFP[x];
// Jeśli osiągnęliśmy
// ujście, to ścieżka
// jest kompletna
if y = t then
begin
// Zwiększamy przepływ
// maksymalny
// o przepustowość
// rezydualną ścieżki -
// wykorzystujemy tablicę
// CFP
inc(fmax, CFP[t]);
// Idziemy wstecz
// po ścieżce
// zwiększając przepływy
// wzdłuż jej krawędzi
// w kierunku zgodnym
/// ze ścieżką oraz
// zmniejszając przepływy
// w kierunku przeciwnym
i := y;
while i <> s do
begin
x := P[i];
inc(F[x][i], CFP[t]);
dec(F[i][x], CFP[t]);
i := x;
end;
// Ustawiamy esc na true,
// co spowoduje wyjście
// z obu pętli
esc := true; break;
end;
// Jeśli wierzchołek y nie
// jest ujściem t, to
// dopisujemy go na końcu
// kolejki Q i kontynuujemy
// pętlę BFS
Q.push(y);
end;
end;
// Tutaj wychodzimy z pętli
// while, jeśli została
// znaleziona ścieżka
//rozszerzająca
if esc then break;
end;
// Jeśli nie znaleziono ścieżki
// rozszerzającej, to esc = false
// i w tym miejscu nastąpi wyjście
// z głównej pętli while
if not esc then break;
end;
// Prezentujemy wyniki obliczeń.
// Najpierw wypisujemy wartość
// maksymalnego przepływu
writeln;
writeln('fmax = ', fmax);
writeln;
// Następnie wypisujemy przepływy
// netto wzdłuż krawędzi
for x := 0 to n-1 do
for y := 0 to n-1 do
if C[x][y] <> 0 then
writeln(x,' -> ',y,' ',
F[x][y],':',C[x][y]);
writeln;
// Usuwamy zmienne dynamiczne
for i := 0 to n-1 do
begin
SetLength(C[i], 0);
SetLength(F[i], 0);
end;
SetLength(C, 0);
SetLength(F, 0);
SetLength(P, 0);
SetLength(CFP, 0);
Q.destroy;
end.
|
// Maksymalny przepływ
// Algorytm Edmondsa-Karpa
// Data: 08.08.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
using namespace std;
const int MAXINT = 2147483647;
// Definicja typu
// elementów listy
//----------------
struct SLel
{
SLel * next;
int data;
};
// Definicja typu
// obiektowego queue
//------------------
class queue
{
private:
SLel * head;
SLel * tail;
public:
// konstruktor
queue();
// destruktor
~queue();
bool empty(void);
int front(void);
void push(int v);
void pop(void);
};
//---------------------
// Metody obiektu queue
//---------------------
// Konstruktor
//------------
queue::queue()
{
head = tail = nullptr;
}
// Destruktor
//-----------
queue::~queue()
{
while(head) pop();
}
// Sprawdza,
// czy kolejka jest pusta
//-----------------------
bool queue::empty(void)
{
return !head;
}
// Zwraca początek kolejki.
// Wartość specjalna
// to -MAXINT
//-------------------------
int queue::front(void)
{
if(head)
return head->data;
else
return -MAXINT;
}
// Zapisuje do kolejki
//--------------------
void queue::push(int v)
{
SLel * p = new SLel;
p->next = NULL;
p->data = v;
if(tail)
tail->next = p;
else
head = p;
tail = p;
}
// Usuwa z kolejki
//----------------
void queue::pop(void)
{
if(head)
{
SLel * p = head;
head = head->next;
if(!head) tail = nullptr;
delete p;
}
}
//---------------
// Program główny
//---------------
int main()
{
// Kolejka
queue Q;
// Macierz przepustowości
int ** C;
// Macierz przepływów netto
int ** F;
// Tablica poprzedników
int * P;
// Tablica przepustowości
// rezydualnych
int * CFP;
// Zmienne proste algorytmu
int n,m,s,t,fmax,cp,x,y,i,j;
// Do wychodzenia
// z zagnieżdżonych pętli
bool esc;
// Ze standardowego wejścia
// odczytujemy:
// n - liczbę wierzchołków
// w grafie sieci
// m - liczbę krawędzi
cin >> n >> m;
// Tworzymy dynamicznie
// macierze:
// C - przepustowości krawędzi
// F - przepływy w krawędziach
C = new int * [n];
F = new int * [n];
for(i = 0; i < n; i++)
{
C[i] = new int [n];
F[i] = new int [n];
}
// Tworzymy dynamicznie tablice:
// P - poprzedniki na ścieżkach
// tworzonych przez BFS
// CFP - przepustowość ścieżek
P = new int [n];
CFP = new int [n];
// Zerujemy macierze przepustowości
// i przepływów
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
C[i][j] = F[i][j] = 0;
// Ze standardowego wejścia
// odczytujemy definicje krawędzi.
// Każda definicja składa się
// z trzech liczb:
// x, y - wierzchołki grafu
// połączone krawędzią
// cp - przepustowość krawędzi
// Odczytane dane zapamiętujemy:
// C[x][y] = cp
for(i = 0; i < m; i++)
{
cin >> x >> y >> cp;
C[x][y] = cp;
}
// Na koniec odczytujemy numer
// wierzchołka źródła s oraz
// numer wierzchołka ujścia t
cin >> s >> t;
//*****************************
//** Tutaj rozpoczyna się **
//** algorytm Edmondsa-Karpa **
//*****************************
// Rozpoczynamy od maksymalnego
// przepływu równego zero
fmax = 0;
// W pętli szukamy ścieżek
// rozszerzających dotąd,
// dopóki istnieją w sieci
// rezydualnej. Każda znaleziona
// ścieżka zwiększa przepływ
// wzdłuż zawartych w niej
// krawędzi grafu sieci
// przepływowej
while(true)
{
// Na początku pętli zerujemy
// tablicę poprzedników P
for(i = 0; i < n; i++)
P[i] = -1;
// źródło nie posiada
// poprzednika. Wpisujemy
// tutaj -2, aby BFS nie
// wybierało źródła
P[s] = -2;
// Do CFP[s] wpisujemy
// największą liczbę
// całkowitą
CFP[s] = MAXINT;
// Zerujemy kolejkę
// i umieszczamy w niej
// źródło s
while(!Q.empty())
Q.pop();
Q.push(s);
// Zmienna esc umożliwia
// odpowiednie wychodzenie
// z dwóch zagnieżdżonych
// pętli - zamiast polecenia
// goto.
esc = false;
// Rozpoczynamy pętlę
// wyszukującą ścieżki BFS.
// Pętla kończy się
// w przypadku braku dalszych
// wierzchołków w kolejce
while(!Q.empty())
{
// Z początku kolejki
// pobieramy element
// i usuwamy go z kolejki
x = Q.front(); Q.pop();
// Sprawdzamy wszystkich
// sąsiadów wierzchołka x
// przeglądając wiersz
// macierzy C
for(y = 0; y < n; y++)
{
// Dla sąsiada y
// wyznaczamy przepustowość
// rezydualną krawędzi x->y.
// Jeśli krawędź nie istnieje
// w sieci, to otrzymamy
// w cp wynik zero
cp = C[x][y]-F[x][y];
// Sprawdzamy, czy krawędź
// istnieje oraz czy
// wierzchołek y nie był
// już wcześniej wybrany
// przez BFS. W takim
// przypadku P[y] ma wartość
// różną od -1.
if(cp && (P[y] == -1))
{
// W P[y] zapamiętujemy,
// iż poprzednikiem y jest x
P[y] = x;
// Dla wierzchołka y
// obliczamy dotychczasową
// przepustowość rezydualną
// ścieżki. Jest to mniejsza
// wartość z przepustowości
// ścieżki dla poprzednika x
// i bieżącej przepustowości
// rezydualnej krawędzi
// x->y.
CFP[y] = CFP[x] > cp
? cp : CFP[x];
// Jeśli osiągnęliśmy
// ujście, to ścieżka jest
// kompletna
if(y == t)
{
// Zwiększamy przepływ
// maksymalny o
// przepustowość
// rezydualną ścieżki
// - wykorzystujemy
// tablicę CFP
fmax += CFP[t];
// Idziemy wstecz
// po ścieżce
// zwiększając przepływy
// wzdłuż jej krawędzi
// w kierunku zgodnym
// ze ścieżką oraz
// zmniejszając
// przepływy w kierunku
// przeciwnym
i = y;
while(i != s)
{
x = P[i];
F[x][i] += CFP[t];
F[i][x] -= CFP[t];
i = x;
}
// Ustawiamy esc na true,
// co spowoduje wyjście
// z obu pętli
esc = true; break;
}
// Jeśli wierzchołek y
// nie jest ujściem t,
// to dopisujemy go na
// końcu kolejki Q
// i kontynuujemy pętlę BFS
Q.push(y);
}
}
// Tutaj wychodzimy z pętli
// while, jeśli została
// znaleziona ścieżka
// rozszerzająca
if(esc) break;
}
// Jeśli nie znaleziono ścieżki
// rozszerzającej, to esc = false
// i w tym miejscu nastąpi wyjście
// z głównej pętli while
if(!esc) break;
}
// Prezentujemy wyniki obliczeń.
// Najpierw wypisujemy wartość
// maksymalnego przepływu
cout << endl << "fmax = "
<< fmax << endl << endl;
// Następnie wypisujemy przepływy
// netto wzdłuż krawędzi
for(x = 0; x < n; x++)
for(y = 0; y < n; y++)
if(C[x][y])
cout << x << " -> "
<< y << " "
<< F[x][y] << ":"
<< C[x][y] << endl;
cout << endl;
// Usuwamy zmienne dynamiczne
for(i = 0; i < n; i++)
{
delete [] C[i];
delete [] F[i];
}
delete [] C;
delete [] F;
delete [] P;
delete [] CFP;
return 0;
}
|
Basic' Maksymalny przepływ
' Algorytm Edmondsa-Karpa
' Data: 08.08.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
Const MAXINT = 2147483647
' Definicja typu elementów listy
'-------------------------------
Type SLel
next As SLel Ptr
data As Integer
End Type
' Definicja typu obiektowego queue
'---------------------------------
Type queue
Private:
head As SLel Ptr
tail As SLel Ptr
Public:
Declare Constructor
Declare Destructor
Declare Function empty As Integer
Declare Function front As Integer
Declare Sub push(ByVal v As Integer)
Declare Sub pop
End Type
'---------------------
' Metody obiektu queue
'---------------------
' Konstruktor
'------------
Constructor queue
head = 0
tail = 0
End Constructor
' Destruktor
'-----------
Destructor queue
While empty = 0
pop
Wend
End Destructor
' Sprawdza, czy kolejka jest pusta
'---------------------------------
Function queue.empty As Integer
If head = 0 Then Return 1
Return 0
End Function
' Zwraca początek kolejki.
' Wartość specjalna to -MAXINT
'-----------------------------
Function queue.front As Integer
If head = 0 then
front = -MAXINT
Else
front = head->data
End If
End Function
' Zapisuje do kolejki
'--------------------
Sub queue.push(ByVal v As Integer)
Dim p As SLel Ptr
p = new SLel
p->next = 0
p->data = v
If tail = 0 Then
head = p
Else
tail->next = p
End If
tail = p
End Sub
' Usuwa z kolejki
'----------------
Sub queue.pop
Dim p As SLel Ptr
If head Then
p = head
head = head->next
If head = 0 Then tail = 0
Delete p
End If
End Sub
'---------------
' Program główny
'---------------
' Kolejka
Dim As queue Q
' Macierz przepustowości
Dim As Integer Ptr Ptr C
' Macierz przepływów netto
Dim As Integer Ptr Ptr F
' Tablica poprzedników
Dim As Integer Ptr P
' Tablica przepustowości rezydualnych
Dim As Integer Ptr CFP
' Zmienne proste algorytmu
Dim As Integer n,m,s,t,fmax,cp,x,y,i,j
' Do wychodzenia z zagnieżdżonych
' pętli
Dim As Byte esc
Open Cons For Input As #1
' Ze standardowego wejścia odczytujemy
' n-liczbę wierzchołków w grafie sieci
' m-liczbę krawędzi
Input #1, n, m
' Tworzymy dynamicznie macierze:
' C - przepustowości krawędzi
' F - przepływy w krawędziach
C = New Integer Ptr [n]
F = New Integer Ptr [n]
For i = 0 To n-1
C[i] = New Integer [n]
F[i] = New Integer [n]
Next
' Tworzymy dynamicznie tablice:
' P - poprzedniki na ścieżkach
' tworzonych przez BFS
' CFP - przepustowość ścieżek
P = New Integer [n]
CFP = New Integer [n]
' Zerujemy macierze przepustowości
' i przepływów
For i = 0 To n-1
For j = 0 To n-1
C[i][j] = 0
F[i][j] = 0
Next
Next
' Ze standardowego wejścia odczytujemy
' definicje krawędzi. Każda definicja
' składa się z trzech liczb:
' x, y - wierzchołki grafu połączone
' krawędzią
' cp - przepustowość krawędzi
' Odczytane dane zapamiętujemy:
' C[x][y] = cp
For i = 1 To m
Input #1, x, y, cp
C[x][y] = cp
Next
' Na koniec odczytujemy numer
' wierzchołka źródła s oraz numer
' wierzchołka ujścia t
Input #1, s, t
Close #1
'*****************************
'** Tutaj rozpoczyna się **
'** algorytm Edmondsa-Karpa **
'*****************************
' Rozpoczynamy od maksymalnego
' przepływu równego zero
fmax = 0
' W pętli szukamy ścieżek
' rozszerzających dotąd, dopóki
' istnieją w sieci rezydualnej.
' Każda znaleziona ścieżka zwiększa
' przepływ wzdłuż zawartych w niej
' krawędzi grafu sieci przepływowej
While 1
' Na początku pętli zerujemy
' tablicę poprzedników P
For i = 0 To n-1
P[i] = -1
Next
' źródło nie posiada poprzednika.
' Wpisujemy tutaj -2, aby BFS nie
' wybierało źródła
P[s] = -2
' Do CFP[s] wpisujemy największą
' liczbę całkowitą
CFP[s] = MAXINT
' Zerujemy kolejkę i umieszczamy
' w niej źródło s
While Q.empty = 0
Q.pop
Wend
Q.push(s)
' Zmienna esc umożliwia odpowiednie
' wychodzenie z dwóch zagnieżdżonych
' pętli - zamiast polecenia goto.
esc = 0
' Rozpoczynamy pętlę wyszukującą
' ścieżki BFS. Pętla kończy się
' w przypadku braku dalszych
' wierzchołków w kolejce
While Q.empty = 0
' Z początku kolejki pobieramy
' element i usuwamy go z kolejki
x = Q.front
Q.pop
' Sprawdzamy wszystkich sąsiadów
' wierzchołka x przeglądając
' wiersz macierzy C
For y = 0 To n-1
' Dla sąsiada y wyznaczamy
' przepustowość rezydualną
' krawędzi x->y. Jeśli krawędź
' nie istnieje w sieci, to
' otrzymamy w cp wynik zero
cp = C[x][y]-F[x][y]
' Sprawdzamy, czy krawędź
' istnieje oraz czy wierzchołek
' y nie był już wcześniej
' wybrany przez BFS. W takim
' przypadku P[y] ma wartość
' różną od -1.
If cp <> 0 AndAlso P[y] = -1 Then
' W P[y] zapamiętujemy, iż
' poprzednikiem y jest x
P[y] = x
' Dla wierzchołka y obliczamy
' dotychczasową przepustowość
' rezydualną ścieżki. Jest to
' mniejsza wartość
' z przepustowości ścieżki dla
' poprzednika x i bieżącej
' przepustowości rezydualnej
' krawędzi x->y.
If CFP[x] > cp Then
CFP[y] = cp
Else
CFP[y] = CFP[x]
EndIf
' Jeśli osiągnęliśmy ujście,
' to ścieżka jest kompletna
If y = t Then
' Zwiększamy przepływ
' maksymalny o przepustowość
' rezydualną ścieżki -
' wykorzystujemy tablicę CFP
fmax += CFP[t]
' Idziemy wstecz po ścieżce
' zwiększając przepływy
' wzdłuż jej krawędzi
' w kierunku zgodnym ze
' ścieżką oraz zmniejszając
' przepływy w kierunku
' przeciwnym
i = y
While i <> s
x = P[i]
F[x][i] += CFP[t]
F[i][x] -= CFP[t]
i = x
Wend
' Ustawiamy esc na true, co
' spowoduje wyjście z obu
' pętli
esc = 1
Exit For
EndIf
' Jeśli wierzchołek y nie jest
' ujściem t, to dopisujemy go
' na końcu kolejki Q
' i kontynuujemy pętlę BFS
Q.push(y)
EndIf
Next
' Tutaj wychodzimy z pętli while,
' jeśli została znaleziona ścieżka
' rozszerzająca
If esc = 1 Then Exit While
Wend
' Jeśli nie znaleziono ścieżki
' rozszerzającej, to esc = false
' i w tym miejscu nastąpi wyjście
' z głównej pętli while
If esc = 0 Then Exit While
Wend
' Prezentujemy wyniki obliczeń.
' Najpierw wypisujemy wartość
' maksymalnego przepływu
Print
Print "fmax =";fmax
Print
' Następnie wypisujemy przepływy netto
' wzdłuż krawędzi
For x = 0 To n-1
For y = 0 To n-1
If C[x][y] <> 0 Then Print Using _
"& -> & &:&";x;y;F[x][y];C[x][y]
Next
Next
Print
' Usuwamy zmienne dynamiczne
for i = 0 To n-1
Delete [] C[i]
Delete [] F[i]
Next
Delete [] C
Delete [] F
Delete [] P
Delete [] CFP
End
|
Python
(dodatek)# Maksymalny przepływ
# Algorytm Edmondsa-Karpa
# Data: 06.04.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------
MAXINT = 2147483647
# Elementy listy
class SLel:
def __init__(self):
self.next = None
self.data = 0
# Obiekt Queue
#-------------
class Queue:
# Konstruktor
def __init__(self):
self.head = None
self.tail = None
# Destruktor
def __del__(self):
while not self.empty():
self.pop()
# Kolejka jest pusta?
def empty(self):
return not self.head
# Zwraca początek kolejki.
# Wartość specjalna to -MAXINT
def front(self):
global MAXINT
if not self.head:
return -MAXINT
else:
return self.head.data
# Zapisuje do kolejki
def push(self,v):
p = SLel()
p.data = v
if not self.tail:
self.head = p
else:
self.tail.next = p
self.tail = p
# Usuwa z kolejki
def pop(self):
if self.head:
self.head = self.head.next
if not self.head:
self.tail = None
#---------------
# Program główny
#---------------
# Kolejka
q = Queue()
# Ze standardowego wejścia odczytujemy
# n — liczbę wierzchołków w grafie sieci
# m — liczbę krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy dynamicznie macierze:
# C — przepustowości krawędzi
# F — przepływy w krawędziach
c = [[0 for j in range(n)] for i in range(n)]
f = [[0 for j in range(n)] for i in range(n)]
# Tworzymy dynamicznie tablice:
# P — poprzedniki na ścieżkach
# tworzonych przez BFS
# CFP — przepustowość ścieżek
pp = [0] * n
cfp = [0] * n
# Ze standardowego wejścia odczytujemy
# definicje krawędzi. Każda definicja
# składa się z trzech liczb:
# x, y — wierzchołki grafu połączone
# krawędzią
# cp — przepustowość krawędzi
# Odczytane dane zapamiętujemy:
# C[x][y] = cp
for i in range(m):
z = input().split()
x = int(z[0])
y = int(z[1])
cp = int(z[2])
c[x][y] = cp
# Na koniec odczytujemy numer
# wierzchołka źródła s oraz numer
# wierzchołka ujścia t
x = input().split()
s = int(x[0])
t = int(x[1])
#*****************************
#** Tutaj rozpoczyna się **
#** algorytm Edmondsa-Karpa **
#*****************************
# Rozpoczynamy od maksymalnego
# przepływu równego zero
fmax = 0
# W pętli szukamy ścieżek
# rozszerzających dotąd, dopóki
# istnieją w sieci rezydualnej.
# Każda znaleziona ścieżka zwiększa
# przepływ wzdłuż zawartych w niej
# krawędzi grafu sieci przepływowej
while True:
# Na początku pętli zerujemy
# tablicę poprzedników PP
pp = [-1] * n
# Źródło nie posiada poprzednika.
# Wpisujemy tutaj -2, aby BFS nie
# wybierało źródła
pp[s] = -2
# Do CFP [s] wpisujemy największą
# liczbę całkowitą
cfp[s] = MAXINT
# Zerujemy kolejkę i umieszczamy
# w niej źródło s
while not q.empty():
q.pop()
q.push(s)
# Zmienna esc umożliwia odpowiednie
# wychodzenie z dwóch zagnieżdżonych
# pętli — zamiast polecenia goto.
esc = False
# Rozpoczynamy pętlę wyszukującą
# ścieżki BFS. Pętla kończy się
# w przypadku braku dalszych
# wierzchołków w kolejce
while not q.empty():
# Z początku kolejki pobieramy
# element i usuwamy go z kolejki
x = q.front()
q.pop()
# Sprawdzamy wszystkich sąsiadów
# wierzchołka x przeglądając
# wiersz macierzy C
for y in range(n):
# Dla sąsiada y wyznaczamy
# przepustowość rezydualną
# krawędzi x->y. Jeśli krawędź
# nie istnieje w sieci, to
# otrzymamy w cp wynik zero
cp = c[x][y] - f[x][y]
# Sprawdzamy, czy krawędź
# istnieje oraz czy wierzchołek
# y nie był już wcześniej
# wybrany przez BFS. W takim
# przypadku PP [y] ma wartość
# różną od -1.
if cp != 0 and pp[y] == -1:
# W PP [y] zapamiętujemy, iż
# poprzednikiem y jest x
pp[y] = x
# Dla wierzchołka y obliczamy
# dotychczasową przepustowość
# rezydualną ścieżki. Jest to
# mniejsza wartość
# z przepustowości ścieżki dla
# poprzednika x i bieżącej
# przepustowości rezydualnej
# krawędzi x->y.
if cfp[x] > cp:
cfp[y] = cp
else:
cfp[y] = cfp[x]
# Jeśli osiągnęliśmy ujście,
# to ścieżka jest kompletna
if y == t:
# Zwiększamy przepływ
# maksymalny o przepustowość
# rezydualną ścieżki —
# wykorzystujemy tablicę CFP
fmax += cfp[t]
# Idziemy wstecz po ścieżce,
# zwiększając przepływy
# wzdłuż jej krawędzi
# w kierunku zgodnym ze
# ścieżką oraz zmniejszając
# przepływy w kierunku
# przeciwnym
i = y
while i != s:
x = pp[i]
f[x][i] += cfp[t]
f[i][x] -= cfp[t]
i = x
# Ustawiamy esc na true, co
# spowoduje wyjście z obu
# pętli
esc = True
break
# Jeśli wierzchołek y nie jest
# ujściem t, to dopisujemy go
# na końcu kolejki Q
# i kontynuujemy pętlę BFS
q.push(y)
# Tutaj wychodzimy z pętli while,
# jeśli została znaleziona ścieżka
# rozszerzająca
if esc: break
# Jeśli nie znaleziono ścieżki
# rozszerzającej, to esc = false
# i w tym miejscu nastąpi wyjście
# z głównej pętli while
if not esc: break
# Prezentujemy wyniki obliczeń.
# Najpierw wypisujemy wartość
# maksymalnego przepływu
print()
print("fmax =",fmax)
print()
# Następnie wypisujemy przepływy netto
# wzdłuż krawędzi
for x in range(n):
for y in range(n):
if c[x][y]:
print(x," -> ",y," ",
f[x][y],":",c[x][y],
sep="")
print()
# Usuwamy zmienne dynamiczne
del c,f,pp,cfp
|
| Wynik: | |
7 11 0 1 7 0 3 3 1 3 4 1 4 6 2 0 9 2 5 9 3 4 9 3 6 2 5 3 3 5 6 6 6 4 8 2 4 fmax = 18 0 -> 1 6:7 0 -> 3 3:3 1 -> 3 0:4 1 -> 4 6:6 2 -> 0 9:9 2 -> 5 9:9 3 -> 4 6:9 3 -> 6 0:2 5 -> 3 3:3 5 -> 6 6:6 6 -> 4 6:8 |
![]() |
K01: fmax ← 0 ; zerujemy wartość przepływu sieciowego K02: Wyzeruj wszystkie przepływy w L K03: Dla u = 1,2,…,n: ; tworzymy strukturę sieci rezydualnej wykonuj kroki K04…K08 K04: Dla każdego sąsiada x wierzchołka u: ; w grafie sieci przepływowej wykonuj kroki K05…K08 K05: Jeśli istnieje z na liście L[x→v] ; dodając krawędzie przeciwne tam, gdzie taki, że x→v = u, to następny obieg pętli K04 K06: Utwórz nowy element listy ; jest to konieczne i przypisz jego adres do z K07: z→v ← u z→c ← 0 z→f ← 0 K08: Dodaj z do listy sąsiedztwa L[x→v] K09: Umieść wartość -1 ; zerujemy poprzedniki na ścieżce rozszerzającej w elementach tablicy P K10: P[s] ← -2 ; to zapobiegnie wybieraniu źródła przez BFS K11: CFP[s] ← ∞ ; w cfp[] przechowujemy przepustowość rezydualną ścieżki K12: Wyczyść kolejkę Q K13: Q.push(s) ; w kolejce umieszczamy źródło s K14: Dopóki ¬ Q.empty(): wykonuj kroki K15…K22 K15: u ← Q.front(); Q.pop() ; do u pobieramy początek kolejki K16: Dla każdego sąsiada x wierzchołka u: wykonuj kroki K17…K22 K17: cp ← x→c - x→f ; wyznaczamy przepustowość rezydualną kanału (x,y.v) K18: Jeśli (cp = 0)(P[x→v] ≠ -1), ; omijamy przy braku krawędzi to wykonaj następny obieg K16 ; lub odwiedzonym sąsiedzie K19: P[x→v] ← u ; zapamiętujemy poprzednik na ścieżce K20: CFP[x→v] ← min(CFP[u],cp) ; obliczamy przepustowość rezydualną do węzła x->v K21: Jeśli x→v = t, ; ścieżka rozszerzająca znaleziona! to idź do kroku K24 K22: Q.push(x→v) K23: Zakończ ; brak ścieżki, kończymy algorytm K24: fmax ← fmax + CFP[t] ; zwiększamy przepływ sieciowy K25: v ← t K26: Dopóki v ≠ s: ; cofamy się po ścieżce rozszerzającej od t do s wykonuj kroki K27…K30 K27: u ← P[v] ; (u,v) jest krawędzią ścieżki rozszerzającej K28: Znajdź na liście L[u] ; w kierunku zgodnym ze ścieżką zwiększamy przepływ element z taki, że z→v = v Dla tego elementu wykonaj: z→f ← z→f + CFP[t] K29: Znajdź na liście L[v] ; w kierunku przeciwnym zmniejszamy przepływ element z taki, że z→v = u Dla tego elementu wykonaj: z→f ← z→f - CFP[t] K30: v ← u ; przechodzimy do następnej krawędzi ścieżki K31: Idź do kroku K09
|
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. |
| Program odczytuje definicję sieci
przepływowej o następującej postaci: Pierwsze dwie liczby określają kolejno liczbę wierzchołków n oraz liczbę krawędzi m sieci przepływowej. Dalej występuje m trójek liczb określających krawędzie sieci. Każda trójka definiuje kolejno: wierzchołek startowy, wierzchołek końcowy oraz przepustowość kanału. W naszym przykładzie przepustowości są liczbami całkowitymi. Na samym końcu danych występują dodatkowo dwie liczby, które definiują źródło s i ujście sieci t. Po odczytaniu definicji sieci program wyznacza maksymalny przepływ oraz przepływy netto w poszczególnych kanałach sieci. |
![]() |
7 11 0 1 7 0 3 3 1 3 4 1 4 6 2 0 9 2 5 9 3 4 9 3 6 2 5 3 3 5 6 6 6 4 8 2 4 |
Pascal// Maksymalny przepływ
// Algorytm Edmondsa-Karpa
// Data: 24.08.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program maxflow;
// Definicja typu
// elementów listy
//----------------
type
PSLel = ^SLel;
SLel = record
next : PSLel;
v, c, f : integer;
end;
// Definicja typu
// obiektowego queue
//------------------
queue = object
private
head : PSLel;
tail : PSLel;
public
constructor init;
destructor destroy;
function empty : boolean;
function front : integer;
procedure push (x : integer);
procedure pop;
end;
//---------------------
// Metody obiektu queue
//---------------------
// Konstruktor
//------------
constructor queue.init;
begin
head := nil;
tail := nil;
end;
// Destruktor
//-----------
destructor queue.destroy;
begin
while not empty do
pop;
end;
// Sprawdza, czy kolejka
// jest pusta
//----------------------
function queue.empty : boolean;
begin
if head = nil then
empty := true
else
empty := false;
end;
// Zwraca początek kolejki
// Wartość specjalna
// to -MAXINT
//------------------------
function queue.front : integer;
begin
if head = nil then
front := -MAXINT
else
front := head^.v;
end;
// Zapisuje do kolejki
//--------------------
procedure queue.push(x : integer);
var
p : PSLel;
begin
new(p);
p^.next := nil;
p^.v := x;
if tail = nil then
head := p
else
tail^.next := p;
tail := p;
end;
// Usuwa z kolejki
//----------------
procedure queue.pop;
var
p : PSLel;
begin
if head <> nil then
begin
p := head;
head := head^.next;
if head = nil then
tail := nil;
dispose(p);
end;
end;
//---------------
// Program główny
//---------------
var
// Liczba krawędzi
// i wierzchołków w grafie
m, n : integer;
// Tablica list sąsiedztwa
L : array of PSLel;
// Numer wierzchołka
// źródła s i ujścia t
s, t : integer;
// Maksymalny przepływ
// sieciowy
fmax : integer;
// Kolejka
Q : queue;
// Tablica poprzedników
P : array of integer;
// Tablica przepustowości
// rezydualnych dla ścieżek
CFP : array of integer;
// Elementy pomocnicze
i, cp, u, v : integer;
// Wskaźniki elementów
// listy sąsiedztwa
x, z : PSLel;
test : boolean;
begin
Q.init;
// Najpierw odczytujemy
// liczbę wierzchołków
// i krawędzi grafu
read(n, m);
// Tworzymy i inicjujemy
// struktury dynamiczne
//----------------------
// Tablica wskaźników list
SetLength(L, n);
for i := 0 to n-1 do
// Tablicę wypełniamy
// pustymi listami
L[i] := nil;
SetLength(P, n);
SetLength(CFP, n);
// Teraz wczytujemy
// definicje poszczególnych
// krawędzi grafu. Każda
// krawędź jest zdefiniowana
// przez trzy liczby u, v i cp:
// u - wierzchołek początkowy
// krawędzi
// v - wierzchołek końcowy
// krawędzi
// cp - przepustowość krawędzi
for i := 1 to m do
begin
// Najpierw odczytujemy
// te trzy liczby
read(u, v, cp);
// Tworzymy nowy
// element listy
new(x);
// Wypełniamy jego
// pola danych
x^.v := v;
x^.c := cp;
// Zerujemy przepływ
x^.f := 0;
// Element dołączamy
// do listy sąsiadów
// wierzchołka u
x^.next := L[u];
L[u] := x;
end;
// Na koniec odczytujemy
// numery wierzchołków
// źródła i ujścia
read(s, t);
//*****************************
//** Tutaj rozpoczyna się **
//** algorytm Edmondsa-Karpa **
//*****************************
// Zerujemy wartość maksymalnego
// przepływu sieciowego
fmax := 0;
// Tworzymy w grafie strukturę
// sieci rezydualnej
for u := 0 to n-1 do
begin
x := L[u];
while x <> nil do
begin
// Sprawdzamy, czy na liście
// sąsiadów x jest wierzchołek u.
// Jeśli tak, to krawędź zwrotna
// istnieje i nie ma potrzeby
// jej tworzenia.
// Zakładamy brak
// krawędzi zwrotnej
test := false;
z := L[x^.v];
while z <> nil do
begin
if z^.v = u then
begin
test := true;
break;
end;
z := z^.next;
end;
// Jeśli brak krawędzi,
// tworzymy ją
if not test then
begin
new(z);
z^.v := u;
z^.c := 0;
z^.f := 0;
z^.next := L[x^.v];
L[x^.v] := z;
end;
x := x^.next;
end;
end;
// Sieć rezydualna
// została utworzona.
// Teraz zajmiemy się
// wyznaczeniem maksymalnych
// przepływów w sieci.
while true do
begin
for i := 0 to n-1 do
// Zerujemy tablicę
// poprzedników
P[i] := -1;
// Przepustowość źródła
// jest nieskończona
CFP[s] := MAXINT;
// Zerujemy kolejkę
while not Q.empty do
Q.pop;
// W kolejce umieszczamy
// źródło s
Q.push(s);
// Szukamy ścieżki w sieci
// rezudualnej od źródła
// s do ujścia t
while not Q.empty do
begin
// Zakładamy brak
// takiej ścieżki
test := false;
// Pobieramy wierzchołek
// z kolejki
u := Q.front; Q.pop;
// Przeglądamy listę
// sąsiadów wierzchołka u
x := L[u];
while x <> nil do
begin
// Wyznaczamy przepustowość
// rezydualną kanału
cp := x^.c-x^.f;
// Przetwarzamy tylko
// istniejące i nieodwiedzone
// jeszcze krawędzie
if (cp <> 0) and
(P[x^.v] = -1) then
begin
// Zapamiętujemy poprzednik
// na ścieżce
P[x^.v] := u;
// Obliczamy przepustowość
// rezydualną do węzła x^.v
if cp < CFP[u] then
CFP[x^.v] := cp
else
CFP[x^.v] := CFP[u];
// Sprawdzamy, czy ścieżka
// sięga do ujścia
if x^.v = t then
begin
// Sygnalizujemy znalezioną
// ścieżkę
test := true;
// Wychodzimy z pętli for
break;
end
else
// Inaczej umieszczamy
// w kolejce wierzchołek
// x^.v
Q.push(x^.v);
end;
x := x^.next;
end;
// Jeśli jest ścieżka,
// wychodzimy z pętli while
if test then break;
end;
// Jeśli brak ścieżki,
// kończymy algorytm
if not test then break;
// Zwiększamy przepływ sieciowy
inc(fmax, CFP[t]);
// Cofamy się po ścieżce
// od ujścia t do źródła s
v := t;
while v <> s do
begin
// u jest poprzednikiem v
// na ścieżce
u := P[v];
// Szukamy na liście
// sąsiadów u krawędzi
// prowadzącej do v.
z := L[u];
while z <> nil do
begin
if z^.v = v then
begin
// W kierunku zgodnym
// ze ścieżką zwiększamy
// przepływ
inc(z^.f, CFP[t]);
break;
end;
z := z^.next;
end;
// Szukamy na liście
// sąsiadów v krawędzi
// prowadzącej do u.
z := L[v];
while z <> nil do
begin
if z^.v = u then
begin
// W kierunku przeciwnym
// do ścieżki zmniejszamy
// przepływ
dec (z^.f, CFP[t]);
break;
end;
z := z^.next;
end;
v := u;
end;
end;
// Prezentujemy wyniki obliczeń.
// Najpierw wypisujemy wartość
// maksymalnego przepływu
writeln;
writeln('fmax = ', fmax);
writeln;
// Następnie wypisujemy
// znalezione przez algorytm
// przepływy w kanałach pierwotnej
// sieci przepływowej. Kanały
// rozpoznajemy po niezerowej
// wartości ich przepustowości.
for i := 0 to n-1 do
begin
z := L[i];
while z <> nil do
begin
if z^.c <> 0 then
writeln(i, ' -> ', z^.v,
' ', z^.f, ':', z^.c);
z := z^.next;
end;
end;
writeln;
// Koniec, usuwamy
// struktury dynamiczne
for i := 0 to n-1 do
begin
x := L[i];
while x <> nil do
begin
z := x;
x := x^.next;
dispose(z);
end;
end;
SetLength(L, 0);
SetLength(P, 0);
SetLength(CFP, 0);
Q.destroy;
end.
|
// Maksymalny przepływ
// Algorytm Edmondsa-Karpa
// Data: 24.08.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
using namespace std;
const int MAXINT = 2147483647;
// Definicja typu elementów listy
//-------------------------------
struct SLel
{
SLel * next;
int v, c, f;
};
// Definicja typu obiektowego queue
//---------------------------------
class queue
{
private:
SLel * head;
SLel * tail;
public:
// konstruktor
queue();
// destruktor
~queue();
bool empty(void);
int front(void);
void push(int v);
void pop(void);
};
//---------------------
// Metody obiektu queue
//---------------------
// Konstruktor
//------------
queue::queue()
{
head = tail = nullptr;
}
// Destruktor
//-----------
queue::~queue()
{
while(head) pop();
}
// Sprawdza,
// czy kolejka jest pusta
//-----------------------
bool queue::empty(void)
{
return !head;
}
// Zwraca początek kolejki.
// Wartość specjalna to -MAXINT
//-----------------------------
int queue::front(void)
{
if(head)
return head->v;
else
return -MAXINT;
}
// Zapisuje do kolejki
//--------------------
void queue::push(int v)
{
SLel * p = new SLel;
p->next = nullptr;
p->v = v;
if(tail) tail->next = p;
else head = p;
tail = p;
}
// Usuwa z kolejki
//----------------
void queue::pop(void)
{
if(head)
{
SLel * p = head;
head = head->next;
if(!head) tail = nullptr;
delete p;
}
}
//---------------
// Program główny
//---------------
int main()
{
// Liczba krawędzi
// i wierzchołków w grafie
int m, n;
// Tablica list sąsiedztwa
SLel ** L;
// Numer wierzchołka
// źródła s i ujścia t
int s, t;
// Maksymalny przepływ sieciowy
int fmax;
// Kolejka
queue Q;
// Tablica poprzedników
int *P;
// Tablica przepustowości
// rezydualnych dla ścieżek
int *CFP;
// Elementy pomocnicze
int i, cp, u, v;
// Wskaźniki elementów
// listy sąsiedztwa
SLel *x, *z;
bool test;
// Najpierw odczytujemy
// liczbę wierzchołków
// i krawędzi grafu
cin >> n >> m;
// Tworzymy i inicjujemy
// struktury dynamiczne
//----------------------
// Tablica wskaźników list
L = new SLel * [n];
for(i = 0; i < n; i++)
// Tablicę wypełniamy
// pustymi listami
L[i] = nullptr;
P = new int [n];
CFP = new int [n];
// Teraz wczytujemy definicje
// poszczególnych krawędzi grafu.
// Każda krawędź jest zdefiniowana
// przez trzy liczby u, v i cp:
// u - wierzchołek początkowy
// krawędzi
// v - wierzchołek końcowy
// krawędzi
// cp - przepustowość krawędzi
for(i = 0; i < m; i++)
{
// Najpierw odczytujemy
// te trzy liczby
cin >> u >> v >> cp;
// Tworzymy nowy element listy
x = new SLel;
// Wypełniamy jego pola danych
x->v = v;
x->c = cp;
// Zerujemy przepływ
x->f = 0;
// Element dołączamy
// do listy sąsiadów
// wierzchołka u
x->next = L[u];
L[u] = x;
}
// Na koniec odczytujemy numery
// wierzchołków źródła i ujścia
cin >> s >> t;
//*****************************
//** Tutaj rozpoczyna się **
//** algorytm Edmondsa-Karpa **
//*****************************
// Zerujemy wartość maksymalnego
// przepływu sieciowego
fmax = 0;
// Tworzymy w grafie strukturę
// sieci rezydualnej
for(u = 0; u < n; u++)
{
for(x = L[u]; x; x = x->next)
{
// Sprawdzamy, czy na liście
// sąsiadów x jest wierzchołek
// u. Jeśli tak, to krawędź
// zwrotna istnieje i nie ma
// potrzeby jej tworzenia.
//----------------------------
// Zakładamy brak krawędzi
// zwrotnej
test = false;
for(z = L[x->v]; z; z = z->next)
if(z->v == u)
{
test = true; break;
}
// Jeśli jest krawędź,
// sprawdzamy kolejnego sąsiada
if(test) continue;
// Tworzymy krawędź zwrotną
z = new SLel;
z->v = u;
z->c = z->f = 0;
z->next = L[x->v];
L[x->v] = z;
}
}
// Sieć rezydualna została utworzona
// Teraz zajmiemy się wyznaczeniem
// maksymalnych przepływów w sieci.
while(true)
{
for(i = 0; i < n; i++)
// Zerujemy tablicę poprzedników
P[i] = -1;
// Przepustowość źródła
// jest nieskończona
CFP[s] = MAXINT;
// Zerujemy kolejkę
while(!Q.empty())
Q.pop();
// W kolejce umieszczamy źródło s
Q.push(s);
// Szukamy ścieżki w sieci
// rezudualnej od źródła s
// do ujścia t
while(!Q.empty())
{
// Zakładamy brak
// takiej ścieżki
test = false;
// Pobieramy wierzchołek
// z kolejki
u = Q.front(); Q.pop();
// Przeglądamy listę sąsiadów
// wierzchołka u
for(x = L[u]; x; x = x->next)
{
// Wyznaczamy przepustowość
// rezydualną kanału
cp = x->c-x->f;
// Przetwarzamy tylko
// istniejące i nieodwiedzone
// jeszcze krawędzie
if(cp && (P[x->v] == -1))
{
// Zapamiętujemy poprzednik
// na ścieżce
P[x->v] = u;
// Obliczamy przepustowość
// rezydualną do węzła x->v
CFP[x->v] = cp < CFP[u] ?
cp : CFP[u];
// Sprawdzamy, czy ścieżka
// sięga do ujścia
if(x->v == t)
{
// Sygnalizujemy
// znalezioną ścieżkę
test = true;
// Wychodzimy z pętli for
break;
}
else
// Inaczej umieszczamy
// w kolejce wierzchołek
// x->v
Q.push(x->v);
}
}
// Jeśli jest ścieżka,
// wychodzimy z pętli while
if(test) break;
}
// Jeśli brak ścieżki,
// kończymy algorytm
if(!test) break;
// Zwiększamy przepływ sieciowy
fmax += CFP[t];
// Cofamy się po ścieżce
// od ujścia t do źródła s
for(v = t; v != s; v = u)
{
// u jest poprzednikiem v
// na ścieżce
u = P[v];
// Szukamy na liście
// sąsiadów u krawędzi
// prowadzącej do v.
for(z = L[u]; z; z = z->next)
if(z->v == v)
{
// W kierunku zgodnym
//ze ścieżką zwiększamy
// przepływ
z->f += CFP[t];
break;
}
// Szukamy na liście
// sąsiadów v krawędzi
// prowadzącej do u.
for(z = L[v]; z; z = z->next)
if(z->v == u)
{
// W kierunku przeciwnym
// do ścieżki zmniejszamy
// przepływ
z->f -= CFP[t];
break;
}
}
}
// Prezentujemy wyniki obliczeń.
// Najpierw wypisujemy wartość
// maksymalnego przepływu
cout << "\nfmax = " << fmax
<< endl << endl;
// Następnie wypisujemy znalezione
// przez algorytm przepływy w
// kanałach pierwotnej sieci
// przepływowej. Kanały rozpoznajemy
// po niezerowej wartości ich
// przepustowości.
for(i = 0; i < n; i++)
for(z = L[i]; z; z = z->next)
if(z->c)
cout << i << " -> " << z->v
<< " " << z->f << ":"
<< z->c << endl;
cout << endl;
// Koniec, usuwamy
// struktury dynamiczne
for(i = 0; i < n; i++)
{
x = L[i];
while(x)
{
z = x;
x = x->next;
delete z;
}
}
delete [] L;
delete [] P;
delete [] CFP;
return 0;
}
|
Basic' Maksymalny przepływ
' Agorytm Edmondsa-Karpa
' Data: 24.08.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
Const MAXINT = 2147483647
' Definicja typu elementów listy
'-------------------------------
Type SLel
next As SLel Ptr
v As Integer
c As Integer
f As Integer
End Type
' Definicja typu obiektowego queue
'---------------------------------
Type queue
Private:
head As SLel Ptr
tail As SLel Ptr
Public:
Declare Constructor
Declare Destructor
Declare Function empty As Integer
Declare Function front As Integer
Declare Sub push(ByVal x As Integer)
Declare Sub pop
End Type
'---------------------
' Metody obiektu queue
'---------------------
' Konstruktor
'------------
Constructor queue
head = 0
tail = 0
End Constructor
' Destruktor-usuwa listę z pamięci
'---------------------------------
Destructor queue
While empty = 0: pop: Wend
End Destructor
' Sprawdza, czy kolejka jest pusta
'---------------------------------
Function queue.empty As Integer
If head = 0 Then Return -1
Return 0
End Function
' Zwraca początek kolejki.
' Wartość specjalna to -MAXINT
'-----------------------------
Function queue.front As Integer
If head = 0 then
front = -MAXINT
Else
front = head->v
End If
End Function
' Zapisuje do kolejki
'--------------------
Sub queue.push(ByVal v As Integer)
Dim p As SLel Ptr
p = new SLel
p->next = 0
p->v = v
If tail = 0 Then
head = p
Else
tail->next = p
End If
tail = p
End Sub
' Usuwa z kolejki
'----------------
Sub queue.pop
Dim p As SLel Ptr
If head <> 0 Then
p = head
head = head->next
If head = 0 Then tail = 0
Delete p
End If
End Sub
'---------------
' Program główny
'---------------
' Liczba krawędzi
' i wierzchołków w grafie
Dim As Integer m, n
' Tablica list sąsiedztwa
Dim As SLel Ptr Ptr L
' Numer wierzchołka
' źródła s i ujścia t
Dim As Integer s, t
' Maksymalny przepływ sieciowy
Dim As Integer fmax
' Kolejka
Dim As queue Q
' Tablica poprzedników
Dim As Integer Ptr P
' Tablica przepustowości
' rezydualnych dla ścieżek
Dim As Integer Ptr CFP
' Elementy pomocnicze
Dim As Integer i, cp, u, v
' Wskaźniki elementów
' listy sąsiedztwa
Dim As SLel Ptr x, z
Dim As Integer test
' Najpierw odczytujemy
' liczbę wierzchołków
' i krawędzi grafu
Open Cons For Input As #1
Input #1, n, m
' Tworzymy i inicjujemy
' struktury dynamiczne
'----------------------
' Tablica wskaźników list
L = New SLel Ptr [n]
For i = 0 To n-1
' Tablicę wypełniamy
' pustymi listami
L[i] = 0
Next
P = New Integer [n]
CFP = New Integer [n]
' Teraz wczytujemy definicje
' poszczególnych krawędzi grafu.
' Każda krawędź jest zdefiniowana
' przez trzy liczby u, v i cp:
' u - wierzchołek początkowy krawędzi
' v - wierzchołek końcowy krawędzi
' cp - przepustowość krawędzi
For i = 1 To m
' Najpierw odczytujemy
' te trzy liczby
Input #1, u, v, cp
' Tworzymy nowy element listy
x = New SLel
' Wypełniamy jego pola danych
x->v = v
x->c = cp
' Zerujemy przepływ
x->f = 0
' Element dołączamy
' do listy sąsiadów
' wierzchołka u
x->next = L[u]
L[u] = x
Next
' Na koniec odczytujemy
' numery wierzchołków
' źródła i ujścia
Input #1, s, t
'*****************************
'** Tutaj rozpoczyna się **
'** algorytm Edmondsa-Karpa **
'*****************************
' Zerujemy wartość maksymalnego
' przepływu sieciowego
fmax = 0
' Tworzymy w grafie strukturę
' sieci rezydualnej
For u = 0 To n-1
x = L[u]
While x <> 0
' Sprawdzamy, czy na liście
' sąsiadów x jest wierzchołek u.
' Jeśli tak, to krawędź zwrotna
' istnieje i nie ma potrzeby
' jej tworzenia.
'-------------------------------
' Zakładamy brak krawędzi zwrotnej
test = 0
z = L[x->v]
While z
If z->v = u Then
test = 1
Exit While
End If
z = z->next
Wend
' Jeśli brak krawędzi,
' tworzymy ją
If test = 0 Then
z = New SLel
z->v = u
z->c = 0
z->f = 0
z->next = L[x->v]
L[x->v] = z
End If
x = x->next
Wend
Next
' Sieć rezydualna została utworzona.
' Teraz zajmiemy się wyznaczeniem
' maksymalnych przepływów w sieci.
While 1
For i = 0 To n-1
' Zerujemy tablicę poprzedników
P[i] = -1
Next
' Przepustowość źródła
' jest nieskończona
CFP[s] = MAXINT
While Not Q.empty
' Zerujemy kolejkę
Q.pop
Wend
' W kolejce umieszczamy źródło s
Q.push(s)
' Szukamy ścieżki
' w sieci rezudualnej
' od źródła s do ujścia t
While Not Q.empty
' Zakładamy brak takiej ścieżki
test = 0
' Pobieramy wierzchołek z kolejki
u = Q.front: Q.pop
' Przeglądamy listę
' sąsiadów wierzchołka u
x = L[u]
While x <> 0
' Wyznaczamy przepustowość
' rezydualną kanału
cp = x->c - x->f
' Przetwarzamy tylko istniejące
' i nieodwiedzone jeszcze krawędzie
if (cp <> 0) AndAlso _
(P[x->v] = -1) Then
' Zapamiętujemy poprzednik
' na ścieżce
P[x->v] = u
' Obliczamy przepustowość
' rezydualną do węzła x->v
If cp < CFP[u] Then
CFP[x->v] = cp
Else
CFP[x->v] = CFP[u]
EndIf
' Sprawdzamy, czy ścieżka
' sięga do ujścia
If x->v = t Then
' Sygnalizujemy znalezioną
' ścieżkę
test = 1
' Wychodzimy z pętli
Exit While
Else
' Inaczej umieszczamy
' w kolejce wierzchołek x->v
Q.push(x->v)
End If
End If
x = x->next
Wend
' Jeśli jest ścieżka,
' wychodzimy z pętli while
If test = 1 Then Exit While
Wend
' Jeśli brak ścieżki,
' kończymy algorytm
If test = 0 Then Exit While
' Zwiększamy przepływ sieciowy
fmax += CFP[t]
' Cofamy się po ścieżce
' od ujścia t do źródła s
v = t
While v <> s
' u jest poprzednikiem v na ścieżce
u = P[v]
' Szukamy na liście sąsiadów u
' krawędzi prowadzącej do v.
z = L[u]
While z
If z->v = v Then
' W kierunku zgodnym
' ze ścieżką zwiększamy
' przepływ
z->f += CFP[t]
Exit While
End If
z = z->next
Wend
' Szukamy na liście sąsiadów v
' krawędzi prowadzącej do u.
z = L[v]
While z
If z->v = u Then
' W kierunku przeciwnym
' do ścieżki zmniejszamy
' przepływ
z->f -= CFP[t]
Exit While
End If
z = z->next
Wend
v = u
Wend
Wend
' Prezentujemy wyniki obliczeń.
' Najpierw wypisujemy wartość
' maksymalnego przepływu
Print
Print "fmax =";fmax
Print
' Następnie wypisujemy znalezione
' przez algorytm przepływy w
' kanałach pierwotnej sieci
' przepływowej. Kanały rozpoznajemy
' po niezerowej wartości ich
' przepustowości.
For i = 0 To n-1
z = L[i]
While z
If z->c Then _
Print Using "& -> & &:&";i; _
z->v;z->f;z->c
z = z->next
Wend
Next
Print
' Koniec, usuwamy
' struktury dynamiczne
For i = 0 To n-1
x = L[i]
While x
z = x
x = x->next
Delete z
Wend
Next
Delete [] L
Delete [] P
Delete [] CFP
End
|
Python
(dodatek)# Maksymalny przepływ
# Agorytm Edmondsa-Karpa
# Data: 07.04.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------
MAXINT = 2147483647
# Definicja elementu listy
# -------------------------
class SLel:
def __init__(self):
self.next = None
self.v = 0
self.c = 0
self.f = 0
# Definicja obiektu Queue
#------------------------
class Queue:
# Konstruktor
def __init__(self):
self.head = None
self.tail = None
# Destruktor
def __del__(self):
while not self.empty():
self.pop()
# Sprawdza,
# czy kolejka jest pusta
def empty(self):
return not self.head
# Zwraca początek kolejki.
# Wartość specjalna to -MAXINT
def front(self):
global MAXINT
if not self.head:
return -MAXINT
else:
return self.head.v
# Zapisuje do kolejki
def push(self, v):
p = SLel()
p.next = None
p.v = v
if not self.tail:
self.head = p
else:
self.tail.next = p
self.tail = p
# Usuwa z kolejki
def pop(self):
if self.head:
self.head = self.head.next
if not self.head:
self.tail = None
#---------------
# Program główny
#---------------
# Kolejka
q = Queue()
# Najpierw odczytujemy
# liczbę wierzchołków
# i krawędzi grafu
r = input().split()
n = int(r[0])
m = int(r[1])
# Tworzymy i inicjujemy
# struktury dynamiczne
#----------------------
# Tablica wskaźników list
tl = [None] * n
tp = [0] * n
cfp = [0] * n
# Teraz wczytujemy definicje
# poszczególnych krawędzi grafu.
# Każda krawędź jest zdefiniowana
# przez trzy liczby u, v i cp:
# u — wierzchołek początkowy krawędzi
# v — wierzchołek końcowy krawędzi
# cp — przepustowość krawędzi
for i in range(m):
# Najpierw odczytujemy
# te trzy liczby
r = input().split()
u = int(r[0])
v = int(r[1])
cp = int(r[2])
# Tworzymy nowy element listy
x = SLel()
# Wypełniamy jego pola danych
x.v = v
x.c = cp
# Zerujemy przepływ
x.f = 0
# Element dołączamy
# do listy sąsiadów
# wierzchołka u
x.next = tl[u]
tl[u] = x
# Na koniec odczytujemy
# numery wierzchołków
# źródła i ujścia
r = input().split()
s = int(r[0])
t = int(r[1])
#*****************************
#** Tutaj rozpoczyna się **
#** algorytm Edmondsa-Karpa **
#*****************************
# Zerujemy wartość maksymalnego
# przepływu sieciowego
fmax = 0
test = False
# Tworzymy w grafie strukturę
# sieci rezydualnej
for u in range(n):
x = tl[u]
while x:
# Sprawdzamy, czy na liście
# sąsiadów x jest wierzchołek u.
# Jeśli tak, to krawędź zwrotna
# istnieje i nie ma potrzeby
# jej tworzenia.
#-------------------------------
# Zakładamy brak
# krawędzi zwrotnej
test = False
z = tl[x.v]
while z:
if z.v == u:
test = True
break
z = z.next
# Jeśli brak krawędzi,
# tworzymy ją
if not test:
z = SLel()
z.v = u
z.c = 0
z.f = 0
z.next = tl[x.v]
tl[x.v] = z
x = x.next
# Sieć rezydualna została utworzona.
# Teraz zajmiemy się wyznaczeniem
# maksymalnych przepływów w sieci.
while True:
tp = [-1] * n
# Przepustowość źródła
# jest nieskończona
cfp[s] = MAXINT
while not q.empty():
# Zerujemy kolejkę
q.pop()
# W kolejce umieszczamy źródło s
q.push(s)
# Szukamy ścieżki
# w sieci rezudualnej
# od źródła s do ujścia t
while not q.empty():
# Zakładamy brak takiej ścieżki
test = False
# Pobieramy wierzchołek z kolejki
u = q.front()
q.pop()
# Przeglądamy listę
# sąsiadów wierzchołka u
x = tl[u]
while x:
# Wyznaczamy przepustowość
# rezydualną kanału
cp = x.c - x.f
# Przetwarzamy tylko
# istniejące
# i nieodwiedzone krawędzie
if cp and tp[x.v] == -1:
# Zapamiętujemy
# poprzednik na ścieżce
tp[x.v] = u
# Obliczamy przepustowość
# rezydualną do węzła x.v
if cp < cfp[u]:
cfp[x.v] = cp
else:
cfp[x.v] = cfp[u]
# Sprawdzamy, czy ścieżka
# sięga do ujścia
if x.v == t:
# Sygnalizujemy
# znalezioną ścieżkę
test = True
# Wychodzimy z pętli
break
else:
# Inaczej umieszczamy
# w kolejce
# wierzchołek x.v
q.push(x.v)
x = x.next
# Jeśli jest ścieżka,
# wychodzimy z pętli while
if test: break
# Jeśli brak ścieżki,
# kończymy algorytm
if not test: break
# Zwiększamy przepływ sieciowy
fmax += cfp[t]
# Cofamy się po ścieżce
# od ujścia t do źródła s
v = t
while v != s:
# u jest poprzednikiem v
# na ścieżce
u = tp[v]
# Szukamy na liście sąsiadów u
# krawędzi prowadzącej do v.
z = tl[u]
while z:
if z.v == v:
# W kierunku zgodnym
# ze ścieżką zwiększamy
# przepływ
z.f += cfp[t]
break
z = z.next
# Szukamy na liście sąsiadów v
# krawędzi prowadzącej do u.
z = tl[v]
while z:
if z.v == u:
# W kierunku przeciwnym
# do ścieżki zmniejszamy
# przepływ
z.f -= cfp[t]
break
z = z.next
v = u
# Prezentujemy wyniki obliczeń.
# Najpierw wypisujemy wartość
# maksymalnego przepływu
print()
print("fmax =",fmax)
print()
# Następnie wypisujemy znalezione
# przez algorytm przepływy w
# kanałach pierwotnej sieci
# przepływowej. Kanały rozpoznajemy
# po niezerowej wartości ich
# przepustowości.
for i in range(n):
z = tl[i]
while z:
if z.c:
print(i," . ",z.v," ",
z.f,":",z.c,sep="")
z = z.next
print()
# Koniec, usuwamy
# struktury dynamiczne
for i in range(n):
while tl[i]:
tl[i] = tl[i].next
del tl,tp,cfp
|
| Wynik: | |
7 11 0 1 7 0 3 3 1 3 4 1 4 6 2 0 9 2 5 9 3 4 9 3 6 2 5 3 3 5 6 6 6 4 8 2 4 fmax = 18 0 -> 3 3:3 0 -> 1 6:7 1 -> 4 6:6 1 -> 3 0:4 2 -> 5 9:9 2 -> 0 9:9 3 -> 6 0:2 3 -> 4 6:9 5 -> 6 6:6 5 -> 3 3:3 6 -> 4 6:8 |
![]() |
![]() |
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.