Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

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

Maksymalny przepływ w sieci
algorytmy Forda-Fulkersona i Edmondsa-Karpa

SPIS TREŚCI
Tematy pomocnicze
Podrozdziały

Problem

Dla danej sieci przepływowej należy określić jej maksymalny przepływ.

Algorytm Forda-Fulkersona

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).
c : przepustowość kanału (ang. capacity).
f : przepływ w kanale (ang. flow), a uv są węzłami sieci przepływowej połączonymi kanałem, dla którego wyznaczamy przepustowość rezydualną.

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ż f(v,u) = -f(u,v). Zatem:

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 sieci

Dopóki w sieci rezydualnej istnieje ścieżka rozszerzająca p, zwiększaj przepływ cf(p) wzdłuż kanałów zgodnych z kierunkiem ścieżki, a zmniejszaj przepływ wzdłuż kanałów przeciwnych (wygaszanie przepływu). Przepływ sieciowy rośnie cf(p).

Aby lepiej zrozumieć ten algorytm, oprzyjmy się na prostym przykładzie.

obrazek 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.
obrazek 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 = {sABt}
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
obrazek 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.
obrazek W nowej sieci rezydualnej szukamy kolejnej
ścieżki rozszerzającej:
p = {sACt}, cf(p) = 3
obrazek 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.
obrazek Szukamy kolejnej ścieżki rozszerzającej:
p = {sDEt}, cf(p) = 6
obrazek W nowej sieci rezydualnej zniknął kanał (D,E).
obrazek Wciąż jednakże możemy znaleźć nową ścieżkę
rozszerzającą:
p = {sDCt }, cf(p) = 3
obrazek 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ł.
obrazek 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 (u,v) i (v,u), to przepływy można bezpośrednio odczytać z kanałów przeciwnych (czyli tych, których nie ma w pierwotnej sieci) w sieci rezydualnej (zysk jest oczywisty – nie musimy zapamiętywać przepustowości kanałów sieci przepływowej). Na przykład przepływ przez kanał (D,E) wynosi:

f(D,E) = cf(E,D) = 6

a przez kanał (C,t) mamy przepływ:

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:

  1. Zastosowanie macierzy sąsiedztwa (ang. adjacency matrix) do reprezentacji sieci, przepustowości oraz przepływów. Sposób ten upraszcza obliczenia, jednakże zwiększa złożoność pamięciową do O(n2). Jeśli sieć przepływowa jest nieduża, można go z powodzeniem stosować. Przepustowości rezydualne kanałów nie musimy pamiętać – obliczamy je jako różnicę przepustowości kanału i przepływu w tym kanale.
  2. Zastosowanie tablicy list sąsiedztwa (ang. adjacency list array), w której umieszczamy listy sąsiadów danego wierzchołka. Element takiej listy oprócz numeru wierzchołka w grafie sieci powinien zawierać informację o przepustowości kanału, przepływie oraz przynależności do sieci (kanały rezydualne mogą nie należeć do sieci przepływowej, stąd konieczność tego rozróżnienia). Sieć rezydualną można zrealizować w tym samym grafie dodając krawędzie przeciwne (o ile krawędź przeciwna już nie istnieje) i ustawiając dla nich przepustowość i przepływ 0 oraz brak przynależności do  sieci pierwotnej (rezydualny kanał przeciwny). Sposób drugi pozwala zaoszczędzić na pamięci, jeśli sieć przepływowa posiada dużo węzłów o niewielkich stopniach. Okupione to zostaje jednakże wzrostem czasu przetwarzania oraz nieznaczną komplikacją algorytmu.

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.


do podrozdziału  do strony 

Algorytm Edmondsa-Karpa

Algorytm Forda-Fulkersona wykorzystujący metodę BFS do wyszukiwania ścieżek rozszerzających w sieci rezydualnej nosi nazwę algorytmu Edmondsa-Karpa.

Algorytm Edmondsa-Karpa wyszukujący maksymalny przepływ - wersja z macierzami sąsiedztwa

Wejście:

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.
: numer wierzchołka będącego ujściem sieci; t ∈ C.

Wyjście

F : macierz n × n przepływów netto.
fmax : wartość maksymalnego przepływu sieciowego.

Elementy pomocnicze:

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.

Lista kroków:

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)obrazek(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 ys, ; 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

Przykładowe programy

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.
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
obrazek 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.
C++
// 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
 obrazek

Algorytm Edmondsa-Karpa wyszukujący maksymalny przepływ – wersja z listami sąsiedztwa

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
L : tablica list sąsiedztwa. Każdy element listy zawiera następujące pola:
  next – adres następnego elementu listy,
  v – numer wierzchołka końcowego,
  c – przepustowość kanału,
  f – przepływ w kanale.
s : numer wierzchołka będącego źródłem; s ∈ C.
t : numer wierzchołka będącego ujściem; t ∈ C.

Wyjście

L : tablica list sąsiedztwa z obliczonymi przepływami netto.
fmax : wartość maksymalnego przepływu sieciowego.

Elementy pomocnicze:

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.
u,v : numery wierzchołków; u,v ∈ C.
x,z : wskaźniki elementów list sąsiedztwa.

Lista kroków:

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[xv] ; dodając krawędzie przeciwne tam, gdzie
         taki, że xv = u, 
         to następny obieg pętli K04
K06:     Utwórz nowy element listy ; jest to konieczne
         i przypisz jego adres do z
K07:     zvu
         zc ← 0
         zf ← 0
K08:     Dodaj z do listy sąsiedztwa L[xv]
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:     cpxc - xf ; wyznaczamy przepustowość rezydualną kanału (x,y.v)
K18:     Jeśli (cp = 0) obrazek (P[xv] ≠ -1), ; omijamy przy braku krawędzi
         to wykonaj następny obieg K16 ; lub odwiedzonym sąsiedzie
K19:     P[xv] ← u ; zapamiętujemy poprzednik na ścieżce
K20:     CFP[xv] ← min(CFP[u],cp) ; obliczamy przepustowość rezydualną do węzła x->v
K21:     Jeśli xv = t, ; ścieżka rozszerzająca znaleziona!
         to idź do kroku K24
K22:     Q.push(xv)
K23: Zakończ ; brak ścieżki, kończymy algorytm
K24: fmaxfmax + CFP[t] ; zwiększamy przepływ sieciowy
K25: vt
K26: Dopóki vs: ; 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 zv = v
       Dla tego elementu
       wykonaj: zf ← zf + CFP[t]
K29:   Znajdź na liście L[v] ; w kierunku przeciwnym zmniejszamy przepływ
       element z taki, że zv = u
       Dla tego elementu
       wykonaj: zf ← zf - CFP[t]
K30:   vu ; przechodzimy do następnej krawędzi ścieżki
K31: Idź do kroku K09

Przykładowe programy

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.
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
obrazek 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.
C++
// 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
 obrazek

do podrozdziału  do strony 

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: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.