Как найти величину максимального потока

Алгоритм Форда-Фалкерсона

Время на прочтение
3 мин

Количество просмотров 41K

Привет, Хабр!
В свободное от учебы время пишу статьи, которых мне не хватало несколько лет назад.

При решении задачи о максимальном потоке я столкнулся с тем, что во всех мне известных источниках было дано формальное описание самих алгоритмов, что очень сильно затрудняло понимание изложенного материала. И в этой статье я попробую на базовом уровне разобрать Алгоритм Форда-Фалкерсона на конкретном примере, чтобы после прочтения данной статьи, вы хотя бы понимали основную суть самого алгоритма.

Постановка задачи

Имеется следующий ориентированный граф, в котором вес ребра обозначает пропускную способность между вершинами. Нужно найти максимальный поток, который можно пропустить из истокаAв стокF.

Исходный граф

Исходный граф

Как работает сам алгоритм?

Следует понимать остаточную сеть как тот же граф, который имеется на входе, но в этом случае мы будем производить над ним некоторые операции:

  1. Отправлять определенное количество потока из текущей вершины в соседние.

  2. Возвращать определенное количество потока из соседних вершин в текущую.

  • В начальный момент времени поток, который мы хотим провести через нашу сеть, должен быть равен нулю. Остаточная сеть совпадает с исходной сетью.

  • Находим любой путь из истока в сток в остаточной сети. Если путь не находим, утверждается, что поток является максимальным.

  • Пускаем через найденный путь поток равный минимальному весу ребра, которое входит в множество рёбер найденного пути.

  • Из веса рёбер на этом пути высчитываем размер потока, который мы пустили.

  • А к весу обратных рёбер (будем считать, что они существуют в остаточной сети и равны 0) прибавляем размер потока. Другими словами, на предыдущем шаге мы отправили некоторое количество потока из текущей вершины в следующую, а теперь при желании можем вернуть это же количество потока обратно в текущую.

  • Возвращаемся обратно к нахождению пути в остаточной сети после модификации.


Разбор конкретного примера

Разобьем одну итерацию проведения потока по выбранному пути на маленькие шаги, чтобы визуально стало понятно, как меняется остаточная сеть после проталкивания очередного потока.

  • Допустим наш алгоритм нашел следующий путь из вершиныAвFнашей остаточной сети, которая на момент начала, совпадает с исходным графом.

Начальная остаточная сеть

Начальная остаточная сеть
  • Сколько потока можем провести по этому пути???
    — Больше2 ед.мы никак не сможем пустить, пропускаем наш поток по этим рёбрам из истока в сток.
    Получаем следующее:

Остаточная сеть после 1-ой итерации

Остаточная сеть после 1-ой итерации

Рёбра с нулевым весом можно удалять. Таким образом, на первой итерации мы смогли увеличить максимальный поток на 2 ед.

Теперь дело за малым, остается всего лишь итерироваться до тех пор, пока существует путь из A в F.

  • Допустим, на 2-ой итерации мы нашли такой путь в нашей остаточной сети:

Остаточная сеть после 1-ой итерации

Остаточная сеть после 1-ой итерации

Пропускаем 3 ед. потока по этому пути, и перестраиваем остаточную сеть.
Получаем следующее:

Остаточная сеть после 2-ой итерации

Остаточная сеть после 2-ой итерации
  • На 3-ей итерации нашли такой путь в нашей модифицированной остаточной сети:

Остаточная сеть после 2-ой итеации

Остаточная сеть после 2-ой итеации

Пускаем 1 ед. потока по этому пути и перестраиваем нашу остаточную сеть.
Получим следующую картину:

Остаточная сеть после 3-ей итерации

Остаточная сеть после 3-ей итерации
  • На 4-ой итерации находим следующий путь в остаточной сети:

Остаточная сеть после 3-ей итерации

Остаточная сеть после 3-ей итерации

Пускаем 4 ед. потока по этому пути и перестраиваем нашу остаточную сеть.
Получим следующую картину:

Итоговая остаточная сеть

Итоговая остаточная сеть

На этом этапе наш алгоритм прекратит выполнение из-за того, что пути из истока в сток не существует. И ответом к поставленной задаче будет служить сумма потоков всех найденных увеличивающихся путей.
Ответ: 10 ед.


Спасибо большое за внимание, надеюсь, был полезен!
Буду рад любым отзывам или поправкам для повышения доступности изложения материала!

Источники

  • Паша и Алгосы

  • Инструмент для работы с графами

Если на сети сформирован некоторый
поток, то для ответа на вопрос: будет ли
он максимальным, используют теорему
Форда-Фалкерсона
.

Теорема 12.1. (Теорема Форда-Фалкерсона)

Максимальный поток в сети равен
минимальной пропускной способности по
всем разрезам:

.

Алгоритм нахождения максимального потока на сети.

Этап 1. Насыщение потока.

Шаг 1. Сформировать произвольный
начальный поток.

Шаг 2. Найти оставшиеся возможные
пути из истока

в сток
,
имеющие только ненасыщенные дуги. Если
такой путь найден, то переходим к шагу
3. Если путь не найден, то переходим к
шагу 4.

Шаг 3. Увеличить поток по
найденному пути таким образом, чтобы
одна из дуг стала насыщенной.

Шаг 4. Получившийся поток насыщен.

Этап 2. Пометка вершин сети.
(Перераспределение потока.)

Шаг 5. Вершину

пометить «».

Шаг 6. Пусть

– любая из уже помеченных вершин;

– некоторая непомеченная вершина,
смежная с вершиной
.
Вершину

помечаем
,
если данные вершины соединены ненасыщенной
дугой
,
и помечаем
,
если соединены непустой дугой
.

После пометки вершин возможны два
случая: вершина

оказалась либо помеченной (шаг 7), либо
непомеченной (шаг 8).

Шаг 7. Вершина

оказалась помеченной. Значит, существует
последовательность помеченных вершин
от

к
.
В этой последовательности каждая
последующая вершина помечена буквой
предыдущей вершины. На дугах
последовательности определяем новый
поток. Увеличиваем на

единиц поток на дугах, имеющих направление
от

к

и уменьшаем на

единиц поток на дугах, имеющих обратное
направление. Число

равно наименьшей разнице между пропускной
способностью и потоком дуг, входящих в
последовательность. Поток можно
увеличивать (уменьшать) на прямых
(обратных) дугах настолько, пока одна
из дуг не станет насыщенной (пустой).
Перераспределение увеличивает поток
на

единиц в вершину
.
Далее вновь переходим к пометке вершин
(шаг 5).

Этап 3. Определение максимального
потока.

Шаг 8. Вершина

осталась непомеченной. Пусть

– множество всех помеченных вершин,

– множество всех непомеченных вершин.
Тогда дуги, связывающие два подмножества
вершин

и
,
определяют разрез
.
Таким образом, найден поток

и разрез
,
для которого выполняется условие
.

Пример 2.
На сети из примера 1. сформировать
максимальный по величине поток,
направленный из истока

в сток
.
Выписать ребра, образующие на сети
разрез минимальной пропускной способности.

Решение. Этап 1.

Шаг 1. Воспользуемся тем, что в
примере 12.1. был уже сформирован некоторый
поток на сети.

Шаг 2. Путь

содержит насыщенную дугу
.
Больше нельзя добавить потока по этому
пути. Пути
,

и

содержат насыщенные дуги.

Но путь

имеет две дуги, которые еще ненасыщенные.

Шаг 3. Увеличиваем поток по
найденному пути:
.
Значит, на этом пути поток увеличиваем
на 1 единицу, и тем самым дуга

стала насыщенной.

Шаг 4. Таким образом, получаем
насыщенный поток, поскольку каждый
рассмотренный путь содержит хотя бы
одну насыщенную дугу.

Этап 2.

Выясним, является ли построенный поток
максимальным по величине. Строим сеть,
на которой отметим все вершины и
ненасыщенные дуги. На этой сети разность
пропускных способностей дуги и проходящего
по ней потока обозначим числом в
квадратных скобках.

Шаг 5, 6. Вершину

пометить
.
На шаге 6 предусматривается пометка
вершин, смежных с вершиной I,
соединенных ненасыщенными дугами. Но
на построенной сети таких вершин нет.

В результате вершина S
оказалась непомеченной.

Этап 3. Шаг 8. Так как
вершина S непомеченная,
то поток, сформированный на сети,
получился максимальным.

Строим разрез на сети. Разбиваем множество
вершин на два подмножества: A
и B. Так как только
одна вершина оказалась помеченной, то
множество A состоит
из одной вершины I, а
остальные образуют множество B:

.

Проводим разрез
,
который состоит из дуг

и
:

.

Определяем величину максимального
потока
:


(ед.) 

Пример 3.
На заданной сети в скобках указаны
пропускные способности дуг. Требуется
сформировать на сети максимальный
поток, направленный из истока

в сток
,
и выписать ребра, образующие на сети
разрез минимальной пропускной способности.

Решение. Этап 1.

Сформируем на сети какой-либо начальный
поток. Рассмотрим путь
.
Поскольку
,
по этому пути пропускаем поток в 2
единицы. На сети значение потока обозначим
числами без скобок. По пути

пропускаем поток в 4 единицы, так как
.
По пути

пропускаем поток в 3 единицы, так как
.
Таким образом, начальный поток имеет
вид:

,

,

.

Объём перевозки 2+4+3=9 ед.

Каждый из рассмотренных путей содержит
насыщенную дугу, поэтому эти пути
насыщенные. На сети есть еще пути, которые
содержат ненасыщенные дуги, а именно:
,

и
.
Для первого пути дополнительно увеличим
поток на 2 единицы, так как
.
Второй и третий пути содержат одну и ту
же дугу

с минимальной для них оставшейся
разностью
.
Поэтому для увеличения потока на 1
единицу выберем, например, путь
.

Теперь каждый из этих путей содержит
насыщенную дугу, следовательно, полученный
поток – насыщенный (объём 12 ед.).

Выясним, является ли построенный поток
максимальным. Изобразим сеть, на которой
отметим все вершины и ненасыщенные
дуги. На этой сети разность пропускной
способности дуги и проходящего по ней
потока обозначим числом в квадратных
скобках. Пропускную способность дуги,
по которой поток не проходит, оставим
в круглых скобках.

Видим, что на сети исток I
и сток S связаны дугами.
Значит, можно добавить какое-то количество
потока по ненасыщенным дугам, при этом
придется перераспределить поток.

Этап 2.

На построенной
сети помечаем вершины. Вершину

пометим
.
Смежную ей вершину

помечаем
,
так как эти вершины соединяет ненасыщенная
дуга
.
Вершину

помечаем
,
так как вершины

и

соединяет ненасыщенная дуга
.
Вершину

помечаем
,
так как вершины

и

соединяет непустая дуга
.
Вершины

и

помечаем
,
так как они соединены с вершиной

ненасыщенными дугами

и
.
Вершина

смежная вершине
,
и эти вершины соединены ненасыщенной
дугой
,
поэтому вершину

помечаем
.
Вершина

смежная вершине
,
и эти вершины соединены ненасыщенной
дугой
,
поэтому вершину

помечаем
.

Вершина

оказалась помеченной. Значит, существует
последовательность помеченных вершин
от

к
:
.
В этой последовательности каждая
последующая вершина помечена буквой
предыдущей вершины. Перераспределим
поток на этом пути.

Определим число
:
 =
min{[3=7-4], [1=3-2], 3, 1, [3=6-3]}=
.
Увеличиваем на 1 единицу поток на дугах,
имеющих направление от

к
:
,
,
,
.
Уменьшаем на 1 единицу поток на дугах,
имеющих обратное направление:
.

Получаем следующую сеть с новым
сформированным потоком, который изображен
на рисунке.

Проверим, будет ли построенный поток
максимальным. Изобразим сеть, на которой
отметим все вершины и ненасыщенные
дуги, как сделали выше.

Вновь помечаем вершины. Вершину

пометим
.
Смежную ей вершину

помечаем
,
так как эти вершины соединяет ненасыщенная
дуга
.
Все остальные вершины, в том числе и
вершина
,
остаются непомеченными. Значит, поток
на сети максимальный.

Этап 3.

Как видно на
последней сети исток

и сток

не связны дугами. Значит, поток,
изображенный на предпоследней сети,
является максимальным.

Строим разрез на
сети. Разбиваем множество вершин на два
подмножества: A
и B.
Помеченные вершины образуют множество
,
непомеченные – множество
.

Проводим
разрез
,
который состоит из дуг
,
,
,
:

.

Определяем величину
максимального потока
:


(ед.)

Соседние файлы в папке Лекции_2

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Improve Article

Save Article

Like Article

  • Read
  • Discuss(110+)
  • Improve Article

    Save Article

    Like Article

    The Ford-Fulkerson algorithm is a widely used algorithm to solve the maximum flow problem in a flow network. The maximum flow problem involves determining the maximum amount of flow that can be sent from a source vertex to a sink vertex in a directed weighted graph, subject to capacity constraints on the edges.

    The algorithm works by iteratively finding an augmenting path, which is a path from the source to the sink in the residual graph, i.e., the graph obtained by subtracting the current flow from the capacity of each edge. The algorithm then increases the flow along this path by the maximum possible amount, which is the minimum capacity of the edges along the path.

    Problem:

    Given a graph which represents a flow network where every edge has a capacity. Also, given two vertices source ‘s’ and sink ‘t’ in the graph, find the maximum possible flow from s to t with the following constraints:

    • Flow on an edge doesn’t exceed the given capacity of the edge.
    • Incoming flow is equal to outgoing flow for every vertex except s and t.

    For example, consider the following graph from the CLRS book. 

    ford_fulkerson1

    The maximum possible flow in the above graph is 23. 

    ford_fulkerson2

    Prerequisite : Max Flow Problem Introduction

    Ford-Fulkerson Algorithm 

     The following is simple idea of Ford-Fulkerson algorithm:

    1. Start with initial flow as 0.
    2. While there exists an augmenting path from the source to the sink:  
      • Find an augmenting path using any path-finding algorithm, such as breadth-first search or depth-first search.
      • Determine the amount of flow that can be sent along the augmenting path, which is the minimum residual capacity along the edges of the path.
      • Increase the flow along the augmenting path by the determined amount.
    3. Return the maximum flow.

    Time Complexity: Time complexity of the above algorithm is O(max_flow * E). We run a loop while there is an augmenting path. In worst case, we may add 1 unit flow in every iteration. Therefore the time complexity becomes O(max_flow * E).

    How to implement the above simple algorithm? 
    Let us first define the concept of Residual Graph which is needed for understanding the implementation. 

    Residual Graph of a flow network is a graph which indicates additional possible flow. If there is a path from source to sink in residual graph, then it is possible to add flow. Every edge of a residual graph has a value called residual capacity which is equal to original capacity of the edge minus current flow. Residual capacity is basically the current capacity of the edge. 

    Let us now talk about implementation details. Residual capacity is 0 if there is no edge between two vertices of residual graph. We can initialize the residual graph as original graph as there is no initial flow and initially residual capacity is equal to original capacity. To find an augmenting path, we can either do a BFS or DFS of the residual graph. We have used BFS in below implementation. Using BFS, we can find out if there is a path from source to sink. BFS also builds parent[] array. Using the parent[] array, we traverse through the found path and find possible flow through this path by finding minimum residual capacity along the path. We later add the found path flow to overall flow. 

    The important thing is, we need to update residual capacities in the residual graph. We subtract path flow from all edges along the path and we add path flow along the reverse edges We need to add path flow along reverse edges because may later need to send flow in reverse direction (See following link for example).
    https://www.geeksforgeeks.org/max-flow-problem-introduction/

    Below is the implementation of Ford-Fulkerson algorithm. To keep things simple, graph is represented as a 2D matrix. 

    C++

    #include <iostream>

    #include <limits.h>

    #include <queue>

    #include <string.h>

    using namespace std;

    #define V 6

    bool bfs(int rGraph[V][V], int s, int t, int parent[])

    {

        bool visited[V];

        memset(visited, 0, sizeof(visited));

        queue<int> q;

        q.push(s);

        visited[s] = true;

        parent[s] = -1;

        while (!q.empty()) {

            int u = q.front();

            q.pop();

            for (int v = 0; v < V; v++) {

                if (visited[v] == false && rGraph[u][v] > 0) {

                    if (v == t) {

                        parent[v] = u;

                        return true;

                    }

                    q.push(v);

                    parent[v] = u;

                    visited[v] = true;

                }

            }

        }

        return false;

    }

    int fordFulkerson(int graph[V][V], int s, int t)

    {

        int u, v;

        int rGraph[V]

                  [V];

        for (u = 0; u < V; u++)

            for (v = 0; v < V; v++)

                rGraph[u][v] = graph[u][v];

        int parent[V];

        int max_flow = 0;

        while (bfs(rGraph, s, t, parent)) {

            int path_flow = INT_MAX;

            for (v = t; v != s; v = parent[v]) {

                u = parent[v];

                path_flow = min(path_flow, rGraph[u][v]);

            }

            for (v = t; v != s; v = parent[v]) {

                u = parent[v];

                rGraph[u][v] -= path_flow;

                rGraph[v][u] += path_flow;

            }

            max_flow += path_flow;

        }

        return max_flow;

    }

    int main()

    {

        int graph[V][V]

            = { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 },

                { 0, 4, 0, 0, 14, 0 },  { 0, 0, 9, 0, 0, 20 },

                { 0, 0, 0, 7, 0, 4 },   { 0, 0, 0, 0, 0, 0 } };

        cout << "The maximum possible flow is "

             << fordFulkerson(graph, 0, 5);

        return 0;

    }

    Java

    import java.io.*;

    import java.lang.*;

    import java.util.*;

    import java.util.LinkedList;

    class MaxFlow {

        static final int V = 6;

        boolean bfs(int rGraph[][], int s, int t, int parent[])

        {

            boolean visited[] = new boolean[V];

            for (int i = 0; i < V; ++i)

                visited[i] = false;

            LinkedList<Integer> queue

                = new LinkedList<Integer>();

            queue.add(s);

            visited[s] = true;

            parent[s] = -1;

            while (queue.size() != 0) {

                int u = queue.poll();

                for (int v = 0; v < V; v++) {

                    if (visited[v] == false

                        && rGraph[u][v] > 0) {

                        if (v == t) {

                            parent[v] = u;

                            return true;

                        }

                        queue.add(v);

                        parent[v] = u;

                        visited[v] = true;

                    }

                }

            }

            return false;

        }

        int fordFulkerson(int graph[][], int s, int t)

        {

            int u, v;

            int rGraph[][] = new int[V][V];

            for (u = 0; u < V; u++)

                for (v = 0; v < V; v++)

                    rGraph[u][v] = graph[u][v];

            int parent[] = new int[V];

            int max_flow = 0;

            while (bfs(rGraph, s, t, parent)) {

                int path_flow = Integer.MAX_VALUE;

                for (v = t; v != s; v = parent[v]) {

                    u = parent[v];

                    path_flow

                        = Math.min(path_flow, rGraph[u][v]);

                }

                for (v = t; v != s; v = parent[v]) {

                    u = parent[v];

                    rGraph[u][v] -= path_flow;

                    rGraph[v][u] += path_flow;

                }

                max_flow += path_flow;

            }

            return max_flow;

        }

        public static void main(String[] args)

            throws java.lang.Exception

        {

            int graph[][] = new int[][] {

                { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 },

                { 0, 4, 0, 0, 14, 0 },  { 0, 0, 9, 0, 0, 20 },

                { 0, 0, 0, 7, 0, 4 },   { 0, 0, 0, 0, 0, 0 }

            };

            MaxFlow m = new MaxFlow();

            System.out.println("The maximum possible flow is "

                               + m.fordFulkerson(graph, 0, 5));

        }

    }

    Python

    from collections import defaultdict

    class Graph:

        def __init__(self, graph):

            self.graph = graph 

            self. ROW = len(graph)

        def BFS(self, s, t, parent):

            visited = [False]*(self.ROW)

            queue = []

            queue.append(s)

            visited[s] = True

            while queue:

                u = queue.pop(0)

                for ind, val in enumerate(self.graph[u]):

                    if visited[ind] == False and val > 0:

                        queue.append(ind)

                        visited[ind] = True

                        parent[ind] = u

                        if ind == t:

                            return True

            return False

        def FordFulkerson(self, source, sink):

            parent = [-1]*(self.ROW)

            max_flow = 0

            while self.BFS(source, sink, parent) :

                path_flow = float("Inf")

                s = sink

                while(s !=  source):

                    path_flow = min (path_flow, self.graph[parent[s]][s])

                    s = parent[s]

                max_flow +=  path_flow

                v = sink

                while(v !=  source):

                    u = parent[v]

                    self.graph[u][v] -= path_flow

                    self.graph[v][u] += path_flow

                    v = parent[v]

            return max_flow

    graph = [[0, 16, 13, 0, 0, 0],

            [0, 0, 10, 12, 0, 0],

            [0, 4, 0, 0, 14, 0],

            [0, 0, 9, 0, 0, 20],

            [0, 0, 0, 7, 0, 4],

            [0, 0, 0, 0, 0, 0]]

    g = Graph(graph)

    source = 0; sink = 5

    print ("The maximum possible flow is %d " % g.FordFulkerson(source, sink))

    C#

    using System;

    using System.Collections.Generic;

    public class MaxFlow {

        static readonly int V = 6;

        bool bfs(int[, ] rGraph, int s, int t, int[] parent)

        {

            bool[] visited = new bool[V];

            for (int i = 0; i < V; ++i)

                visited[i] = false;

            List<int> queue = new List<int>();

            queue.Add(s);

            visited[s] = true;

            parent[s] = -1;

            while (queue.Count != 0) {

                int u = queue[0];

                queue.RemoveAt(0);

                for (int v = 0; v < V; v++) {

                    if (visited[v] == false

                        && rGraph[u, v] > 0) {

                        if (v == t) {

                            parent[v] = u;

                            return true;

                        }

                        queue.Add(v);

                        parent[v] = u;

                        visited[v] = true;

                    }

                }

            }

            return false;

        }

        int fordFulkerson(int[, ] graph, int s, int t)

        {

            int u, v;

            int[, ] rGraph = new int[V, V];

            for (u = 0; u < V; u++)

                for (v = 0; v < V; v++)

                    rGraph[u, v] = graph[u, v];

            int[] parent = new int[V];

            int max_flow = 0;

            while (bfs(rGraph, s, t, parent)) {

                int path_flow = int.MaxValue;

                for (v = t; v != s; v = parent[v]) {

                    u = parent[v];

                    path_flow

                        = Math.Min(path_flow, rGraph[u, v]);

                }

                for (v = t; v != s; v = parent[v]) {

                    u = parent[v];

                    rGraph[u, v] -= path_flow;

                    rGraph[v, u] += path_flow;

                }

                max_flow += path_flow;

            }

            return max_flow;

        }

        public static void Main()

        {

            int[, ] graph = new int[, ] {

                { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 },

                { 0, 4, 0, 0, 14, 0 },  { 0, 0, 9, 0, 0, 20 },

                { 0, 0, 0, 7, 0, 4 },   { 0, 0, 0, 0, 0, 0 }

            };

            MaxFlow m = new MaxFlow();

            Console.WriteLine("The maximum possible flow is "

                              + m.fordFulkerson(graph, 0, 5));

        }

    }

    Javascript

    <script>

    let V = 6;

    function bfs(rGraph, s, t, parent)

    {

        let visited = new Array(V);

        for(let i = 0; i < V; ++i)

            visited[i] = false;

        let queue  = [];

        queue.push(s);

        visited[s] = true;

        parent[s] = -1;

        while (queue.length != 0)

        {

            let u = queue.shift();

            for(let v = 0; v < V; v++)

            {

                if (visited[v] == false &&

                    rGraph[u][v] > 0)

                {

                    if (v == t)

                    {

                        parent[v] = u;

                        return true;

                    }

                    queue.push(v);

                    parent[v] = u;

                    visited[v] = true;

                }

            }

        }

        return false;

    }

    function fordFulkerson(graph, s, t)

    {

        let u, v;

        let rGraph = new Array(V);

        for(u = 0; u < V; u++)

        {

            rGraph[u] = new Array(V);

            for(v = 0; v < V; v++)

                rGraph[u][v] = graph[u][v];

         }

        let parent = new Array(V);

        let max_flow = 0;

        while (bfs(rGraph, s, t, parent))

        {

            let path_flow = Number.MAX_VALUE;

            for(v = t; v != s; v = parent[v])

            {

                u = parent[v];

                path_flow = Math.min(path_flow,

                                     rGraph[u][v]);

            }

            for(v = t; v != s; v = parent[v])

            {

                u = parent[v];

                rGraph[u][v] -= path_flow;

                rGraph[v][u] += path_flow;

            }

            max_flow += path_flow;

        }

        return max_flow;

    }

    let graph = [ [ 0, 16, 13, 0, 0, 0 ],

                  [ 0, 0, 10, 12, 0, 0 ],

                  [ 0, 4, 0, 0, 14, 0 ], 

                  [ 0, 0, 9, 0, 0, 20 ],

                  [ 0, 0, 0, 7, 0, 4 ],  

                  [ 0, 0, 0, 0, 0, 0 ] ];

    document.write("The maximum possible flow is " +

                   fordFulkerson(graph, 0, 5));

    </script>

    Output

    The maximum possible flow is 23

    Time Complexity : O(|V| * E^2) ,where E is the number of edges and V is the number of vertices.

    Space Complexity :O(V) , as we created queue.

    The above implementation of Ford Fulkerson Algorithm is called Edmonds-Karp Algorithm. The idea of Edmonds-Karp is to use BFS in Ford Fulkerson implementation as BFS always picks a path with minimum number of edges. When BFS is used, the worst case time complexity can be reduced to O(VE2). The above implementation uses adjacency matrix representation though where BFS takes O(V2) time, the time complexity of the above implementation is O(EV3) (Refer CLRS book for proof of time complexity)

    This is an important problem as it arises in many practical situations. Examples include, maximizing the transportation with given traffic limits, maximizing packet flow in computer networks.
    Dinc’s Algorithm for Max-Flow.

    Exercise: 
    Modify the above implementation so that it that runs in O(VE2) time.

    Last Updated :
    09 Apr, 2023

    Like Article

    Save Article

    Сеть состоит из ориентированного графа G=(V={1,…,n}, A={(i,j)},i,j∈V)G = (V = {1, dots, n},~A = {(i,j)}, i,j in V) с двумя выделенными вершинами ss — источник и tt — сток. Каждая дуга обладает пропускной способностью uiju_{ij}. (i,j)∈A:uij>0(i,j) in A :u_{ij} gt 0

    Поток: fij,(i,j)∈Af_{ij}, (i,j) in A и его свойства:

    1. fij≥0f_{ij} ge 0
    2. fij≤uij ∀(i,j)∈Af_{ij} le u_{ij} ~ forall (i,j) in A
    3. ∑j:(j,i0)∈Afji0−∑j:(i0,j)∈Afi0j=0, ∀i0∈V,i0≠s,tsumlimits_{j:(j,i_0) in A}f_{ji_0} — sumlimits_{j:(i_0,j) in A}f_{i_0j} = 0, ~forall i_0 in V, i_0neq s,t — сохранение потока

    ∑(s,j)∈Afsj−∑(j,s)∈Afjs=M(f)sumlimits_{(s,j) in A}f_{sj} — sumlimits_{(j,s) in A}f_{js} = M(f) — величина потока (Суммарный поток выходящий из истока минус суммарный поток входящий в исток)

    Задача о максимальном потоке

    Необходимо найти поток fi,j′f_{i,j}’, такой что M(f′)=max⁡fijM(f) M(f’) = maxlimits_{f_{ij}}M(f), то есть величина потока максимальна.

    Пример сети с построенным максимальным потоком

    img

    Алгоритм Форда-Фалкерсона

    Пусть πpi — это путь

    по прямой дуге: δij=uij−fij>0delta_{ij} = u_{ij} — f_{ij} gt 0 fij=fij+δ(π)f_{ij} = f_{ij} + delta(pi)

    По обратной дуге: δkj=fkj>0delta_{kj} = f_{kj} gt 0 fkj=fkj−δ(π)f_{kj} = f_{kj} — delta(pi)

    δ(π)=min⁡(i,j)∈πδi,jdelta(pi) = minlimits_{(i,j) in pi}delta_{i,j}

    Увеличивающий путь — это путь πpi (последовательность дуг из истока в сток), для которого величина δ(π)>0delta(pi) gt 0, которая определяется следующим образом: для каждой дуги (i,j)(i,j) входящей в путь πpi вычисляется величина δij={uij−fij>0,прямая дугаfji>0,обратная дугаdelta_{ij} = left{ begin{matrix} u_{ij} — f_{ij} > 0 & ,прямая ~ дуга\ f_{ji} > 0 & ,обратная ~ дуга end{matrix} right. и δ(π)=min⁡(i,j)∈πδijdelta(pi) = minlimits_{(i,j) in pi} delta_{ij}.

    неформально это путь из истока в сток, вдоль которого можно увеличить поток на некоторую величину.

    Про обратную дугу — можно забирать поток назад

    Алгоритм

    1. fijf_{ij} — начальный поток (fij=0f_{ij}=0)
    2. Увеличить путь πpi на δ(π)delta(pi) [итеративно]
    3. Если нет увеличивающего пути — остановка. [нашли максимальный поток]

    Пусть uij∈Nu_{ij} in N — закончится, так как как минимум на 1 каждый раз увеличивается, поэтому алгоритм не зациклится.

    Алгоритм Карзанова

    G(f)=(V,A(f))G(f) = (V, A(f))остаточная сеть

    A(f)A(f):

    1. (i,j)∈A,fij<uij(i,j) in A, f_{ij} lt u_{ij} (i,j)→A(f)(i,j) to A(f) vij=uij−fijv_{ij} = u_{ij} — f_{ij}
    2. (i,j)∈A,fij>0(i,j) in A, f_{ij} gt 0 (j,i)→A(f)(j,i) to A(f) vji=fijv_{ji} = f_{ij}

    Слоистая сеть G∗(f)=(V∗,A∗(f))G^*(f)= (V^*, A^*(f)) включает в себя множество всех кратчайших путей (по числу дуг) из источника в сток по слоям:

    Нулевой слой V0={s}V_0 = {s}

    Первый слой V1={i:(s,i)∈A(f)}V_1 = { i : (s,i) in A(f)}

    Второй слой V2={j:(j,i)∈A(f),i∈V1,j∉V1∪V0V_2 = {j: (j,i) in A(f), i in V_1, j notin V_1 cup V_0 и так далее, на последнем слое может быть не только сток, в промежуточных может быть тупиковые, поэтому надо убирать висячие узлы как только дошли до стока и инцидентные им дуги.

    Тупиковый поток — поток, относительно которого нет прямого увеличивающего пути (содержит только прямые дуги) из источника в сток.

    Алгоритм

    1. Начальный нулевой поток в G
    2. Построить G(f) — остаточную сеть
    3. Если нет прямого пути из s в t, то стоп ff — максимальный поток
    4. Построить G∗(f)G^*(f) — слоистую сеть
    5. Построить тупиковый поток в G∗(f)G^*(f)gijg_{ij}
    6. Изменить потоки: (i,j)∈A∗(f)(i,j) in A^*(f) — прямая fij=fij+gijf_{ij} = f_{ij} + g_{ij} , для обратных дуг (i,j)∈A∗(f)(i,j) in A^*(f) fij=fij−gijf_{ij} = f_{ij} — g_{ij}
    7. Перейти на 2 шаг

    Разрез: , V=Vc∪V‾cV = V_c cup overline{V}_c, Vc∩V‾c=∅V_c cap overline{V}_c = empty, s∈Vcs in V_c, t∈V‾ct in overline{V}_c

    u(Vc,V‾c)=∑i∈Vc,j∈V‾cuiju(V_c, overline{V}_c) = sumlimits_{i in V_c, j in overline{V}_c} u_{ij} — пропускная способность разреза

    Разрез с минимальной пропускной способностью называется минимальным разрезом.

    Теорема о максимальном потоке и минимальном разрезе. Величина максимального потока в сети равна величине минимального разреза в сети.

    Задача составления допустимого расписания с прерываниями для многопроцессорной системы:

    Пусть имеется mm — процессоров. Задано N={1,…,n}N = {1,…,n } — число работ и для каждой работы заданы tit_i — длительность , (bi,fi](b_i, f_i] — директивный интервал ii-ой работы (bi<fib_i lt f_i и ti≤fi−bit_i le f_i — b_i). Во время выполнения работ допускаются прерывания и переключения. Требуется ответить на вопрос существует ли допустимое расписание и как его построить.

    Сведение задачи к задачи поиска максимального потока в сети:

    Обозначим y0<y1<y2<…ypy_0 lt y_1 lt y_2 lt … y_p — все упорядоченные значения bi,fib_i, f_i

    Составим отрезки Ij=(yj−1,yj],j=1,p‾I_j=(y_{j-1}, y_j], j=overline{1,p}

    Сеть будет состоять из узлов ss, tt , IjI_j, wiw_i

    Δj=yj−yj−1Delta_j = y_j — y_{j-1} — длительность интервала IjI_j

    Добавляем дуги (s,Ij)(s, I_j) с пропускной способностью mΔjmDelta_j

    Добавляем дуги (Ij,wi)(I_j,w_i), если Ij⊂(bi,fi]I_j sub (b_i, f_i] пропускной способностью ΔjDelta_j

    Добавляем дуги (wi,s)(w_i, s) пропускной способностью tit_i

    Пример сети для задачи при m=2m=2

    Раб.bifiti1163215330634043begin{matrix}Раб. & b_i & f_i & t_i \
    1 & 1 & 6 &3 \
    2 & 1 & 5 &3 \
    3 & 0 & 6 & 3\
    4 & 0 & 4 & 3\
    end{matrix}

    I1=[0,1],I2=[1,4],I3=[4,5],I4=[5,6]I_1 = [0,1], I_2 = [1,4], I_3 = [4,5], I_4=[5,6]

    img

    Теорема. Допустимое расписание существует, тогда и только тогда, когда Максимальный поток в G насыщает все выходные дуги.

    Как можно искать увеличивающий путь

    Есть состояния у каждого узла : Н,ПН,ПП{Н,ПН,ПП} — непомеченный, помеченный непросмотренный, помеченный просмотренный

    Метим следующие для рассмотрения вершины по которым теоретически можно получить увеличивающий путь

    1. S [-], S — ПНПН
    2. Если нет ПНПН, то останавливаемся (нет увеличивающих путей)
    3. i−ПНi — ПН, ∀j−Н,(i,j)∈A,fij<uijforall j — Н, (i,j) in A, f_{ij} lt u_{ij} — метим [i][i], j−ПНj — ПН

    i−ПНi — ПН, ∀j−Н,(i,j)∈A,fij>0forall j — Н, (i,j) in A, f_{ij} gt 0 — метим [i][i], j−ПНj — ПН

    ​ далее i−ППi — ПП

    1. Если t−Нt — Н, то переходим на шаг 2

    ​ Если t−Пt — П — построить увеличивающий путь от истока к стоку

    Как строить тупиковый поток

    i∈V∗(f),i≠s,ti in V^*(f), ineq s,t

    a(i)=min⁡{∑(j,i)∈A∗(f)vji,∑(j,i)∈A∗(f)vij}a(i)= min{ sumlimits_{(j,i) in A^*(f)}v_{ji}, sumlimits_{(j,i) in A^*(f)}v_{ij}} пропускная способность узла

    a(s)=∑(s,j)∈A∗(f)vsja(s) = sumlimits_{(s,j) in A^*(f)}v_{sj}

    a(s)=∑(j,s)∈A∗(f)vjta(s) = sumlimits_{(j,s) in A^*(f)}v_{jt}

    i0i_0 — самый слабый узел a(i0)=min⁡i∈V∗(f)a(i)a(i_0) = minlimits_{i in V^*(f)}a(i)

    Выбираем его

    ai0a_{i_0} — будем проталкивать поток от i0i_0 до стока и до истока (можно по разным дугам, но разрешена только одна недонасыщенная)

    После этого вершина i0i_0 исключается из сети и все полностью насыщенные дуги (если дуги были не полностью насыщены и остались в сети то им изменяем пропускную способность)

    и так сначала, пока не останется тупиковый поток

    Понравилась статья? Поделить с друзьями:

    Не пропустите также:

  • Почему дублируются контакты в андроид как исправить хуавей
  • Decompression problem uncompressed block size is too big как исправить gta 5
  • Как в ворде исправить нумерацию списка
  • Как найти ответы на тест по истории
  • Createtarfork process ended with error 255 как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии