📜  最大流量问题的 Ford-Fulkerson 算法

📅  最后修改于: 2022-05-13 01:57:54.121000             🧑  作者: Mango

最大流量问题的 Ford-Fulkerson 算法

给定一个表示流网络的图,其中每条边都有容量。同样给定图中的两个顶点source 's' 和sink 't',找到从 s 到 t 的最大可能流,具有以下约束:
a)边上的流量不超过边的给定容量。
b)对于除 s 和 t 之外的每个顶点,流入流等于流出流。

例如,考虑 CLRS 书中的下图。

福特富尔克森1

上图中的最大可能流量为 23。

福特富尔克森2

先决条件:最大流量问题介绍

Ford-Fulkerson Algorithm 
The following is simple idea of Ford-Fulkerson algorithm:
1) Start with initial flow as 0.
2) While there is a augmenting path from source to sink. 
           Add this path-flow to flow.
3) Return flow.

时间复杂度:上述算法的时间复杂度为 O(max_flow * E)。当有一条增广路径时,我们运行一个循环。在最坏的情况下,我们可能会在每次迭代中添加 1 个单位流。因此时间复杂度变为 O(max_flow * E)。

如何实现上述简单算法?
让我们首先定义理解实现所需的残差图的概念。

流网络的残差图是指示附加可能流的图。如果残差图中存在从源到汇的路径,则可以添加流。残差图的每条边都有一个称为剩余容量的值,该值等于边的原始容量减去当前流量。剩余容量基本上是边缘的当前容量。
现在让我们谈谈实现细节。如果残差图的两个顶点之间没有边,则残差容量为0。我们可以将残差图初始化为原始图,因为没有初始流并且初始剩余容量等于原始容量。要找到增广路径,我们可以对残差图进行 BFS 或 DFS。我们在下面的实现中使用了 BFS。使用 BFS,我们可以查明是否存在从源到接收器的路径。 BFS 还构建 parent[] 数组。使用 parent[] 数组,我们遍历找到的路径,并通过找到沿路径的最小剩余容量来找到通过该路径的可能流。我们稍后将找到的路径流添加到整体流中。

重要的是,我们需要更新残差图中的残差容量。我们从沿路径的所有边中减去路径流,然后沿反向边缘添加路径流 我们需要沿反向边缘添加路径流,因为以后可能需要反向发送流(例如,请参见以下链接)。
https://www.geeksforgeeks.org/max-flow-problem-introduction/

下面是 Ford-Fulkerson 算法的实现。为简单起见,图表示为 2D 矩阵。

C++
// C++ program for implementation of Ford Fulkerson
// algorithm
#include 
#include 
#include 
#include 
using namespace std;
 
// Number of vertices in given graph
#define V 6
 
/* Returns true if there is a path from source 's' to sink
  't' in residual graph. Also fills parent[] to store the
  path */
bool bfs(int rGraph[V][V], int s, int t, int parent[])
{
    // Create a visited array and mark all vertices as not
    // visited
    bool visited[V];
    memset(visited, 0, sizeof(visited));
 
    // Create a queue, enqueue source vertex and mark source
    // vertex as visited
    queue q;
    q.push(s);
    visited[s] = true;
    parent[s] = -1;
 
    // Standard BFS Loop
    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 we find a connection to the sink node,
                // then there is no point in BFS anymore We
                // just have to set its parent and can return
                // true
                if (v == t) {
                    parent[v] = u;
                    return true;
                }
                q.push(v);
                parent[v] = u;
                visited[v] = true;
            }
        }
    }
 
    // We didn't reach sink in BFS starting from source, so
    // return false
    return false;
}
 
// Returns the maximum flow from s to t in the given graph
int fordFulkerson(int graph[V][V], int s, int t)
{
    int u, v;
 
    // Create a residual graph and fill the residual graph
    // with given capacities in the original graph as
    // residual capacities in residual graph
    int rGraph[V]
              [V]; // Residual graph where rGraph[i][j]
                   // indicates residual capacity of edge
                   // from i to j (if there is an edge. If
                   // rGraph[i][j] is 0, then there is not)
    for (u = 0; u < V; u++)
        for (v = 0; v < V; v++)
            rGraph[u][v] = graph[u][v];
 
    int parent[V]; // This array is filled by BFS and to
                   // store path
 
    int max_flow = 0; // There is no flow initially
 
    // Augment the flow while there is path from source to
    // sink
    while (bfs(rGraph, s, t, parent)) {
        // Find minimum residual capacity of the edges along
        // the path filled by BFS. Or we can say find the
        // maximum flow through the path found.
        int path_flow = INT_MAX;
        for (v = t; v != s; v = parent[v]) {
            u = parent[v];
            path_flow = min(path_flow, rGraph[u][v]);
        }
 
        // update residual capacities of the edges and
        // reverse edges along the path
        for (v = t; v != s; v = parent[v]) {
            u = parent[v];
            rGraph[u][v] -= path_flow;
            rGraph[v][u] += path_flow;
        }
 
        // Add path flow to overall flow
        max_flow += path_flow;
    }
 
    // Return the overall flow
    return max_flow;
}
 
// Driver program to test above functions
int main()
{
    // Let us create a graph shown in the above example
    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
// Java program for implementation of Ford Fulkerson
// algorithm
import java.io.*;
import java.lang.*;
import java.util.*;
import java.util.LinkedList;
 
class MaxFlow {
    static final int V = 6; // Number of vertices in graph
 
    /* Returns true if there is a path from source 's' to
      sink 't' in residual graph. Also fills parent[] to
      store the path */
    boolean bfs(int rGraph[][], int s, int t, int parent[])
    {
        // Create a visited array and mark all vertices as
        // not visited
        boolean visited[] = new boolean[V];
        for (int i = 0; i < V; ++i)
            visited[i] = false;
 
        // Create a queue, enqueue source vertex and mark
        // source vertex as visited
        LinkedList queue
            = new LinkedList();
        queue.add(s);
        visited[s] = true;
        parent[s] = -1;
 
        // Standard BFS Loop
        while (queue.size() != 0) {
            int u = queue.poll();
 
            for (int v = 0; v < V; v++) {
                if (visited[v] == false
                    && rGraph[u][v] > 0) {
                    // If we find a connection to the sink
                    // node, then there is no point in BFS
                    // anymore We just have to set its parent
                    // and can return true
                    if (v == t) {
                        parent[v] = u;
                        return true;
                    }
                    queue.add(v);
                    parent[v] = u;
                    visited[v] = true;
                }
            }
        }
 
        // We didn't reach sink in BFS starting from source,
        // so return false
        return false;
    }
 
    // Returns the maximum flow from s to t in the given
    // graph
    int fordFulkerson(int graph[][], int s, int t)
    {
        int u, v;
 
        // Create a residual graph and fill the residual
        // graph with given capacities in the original graph
        // as residual capacities in residual graph
 
        // Residual graph where rGraph[i][j] indicates
        // residual capacity of edge from i to j (if there
        // is an edge. If rGraph[i][j] is 0, then there is
        // not)
        int rGraph[][] = new int[V][V];
 
        for (u = 0; u < V; u++)
            for (v = 0; v < V; v++)
                rGraph[u][v] = graph[u][v];
 
        // This array is filled by BFS and to store path
        int parent[] = new int[V];
 
        int max_flow = 0; // There is no flow initially
 
        // Augment the flow while there is path from source
        // to sink
        while (bfs(rGraph, s, t, parent)) {
            // Find minimum residual capacity of the edhes
            // along the path filled by BFS. Or we can say
            // find the maximum flow through the path found.
            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]);
            }
 
            // update residual capacities of the edges and
            // reverse edges along the path
            for (v = t; v != s; v = parent[v]) {
                u = parent[v];
                rGraph[u][v] -= path_flow;
                rGraph[v][u] += path_flow;
            }
 
            // Add path flow to overall flow
            max_flow += path_flow;
        }
 
        // Return the overall flow
        return max_flow;
    }
 
    // Driver program to test above functions
    public static void main(String[] args)
        throws java.lang.Exception
    {
        // Let us create a graph shown in the above example
        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
# Python program for implementation
# of Ford Fulkerson algorithm
from collections import defaultdict
 
# This class represents a directed graph
# using adjacency matrix representation
class Graph:
 
    def __init__(self, graph):
        self.graph = graph  # residual graph
        self. ROW = len(graph)
        # self.COL = len(gr[0])
 
    '''Returns true if there is a path from source 's' to sink 't' in
    residual graph. Also fills parent[] to store the path '''
 
    def BFS(self, s, t, parent):
 
        # Mark all the vertices as not visited
        visited = [False]*(self.ROW)
 
        # Create a queue for BFS
        queue = []
 
        # Mark the source node as visited and enqueue it
        queue.append(s)
        visited[s] = True
 
         # Standard BFS Loop
        while queue:
 
            # Dequeue a vertex from queue and print it
            u = queue.pop(0)
 
            # Get all adjacent vertices of the dequeued vertex u
            # If a adjacent has not been visited, then mark it
            # visited and enqueue it
            for ind, val in enumerate(self.graph[u]):
                if visited[ind] == False and val > 0:
                      # If we find a connection to the sink node,
                    # then there is no point in BFS anymore
                    # We just have to set its parent and can return true
                    queue.append(ind)
                    visited[ind] = True
                    parent[ind] = u
                    if ind == t:
                        return True
 
        # We didn't reach sink in BFS starting
        # from source, so return false
        return False
             
     
    # Returns the maximum flow from s to t in the given graph
    def FordFulkerson(self, source, sink):
 
        # This array is filled by BFS and to store path
        parent = [-1]*(self.ROW)
 
        max_flow = 0 # There is no flow initially
 
        # Augment the flow while there is path from source to sink
        while self.BFS(source, sink, parent) :
 
            # Find minimum residual capacity of the edges along the
            # path filled by BFS. Or we can say find the maximum flow
            # through the path found.
            path_flow = float("Inf")
            s = sink
            while(s !=  source):
                path_flow = min (path_flow, self.graph[parent[s]][s])
                s = parent[s]
 
            # Add path flow to overall flow
            max_flow +=  path_flow
 
            # update residual capacities of the edges and reverse edges
            # along the path
            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
 
  
# Create a graph given in the above diagram
 
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))
 
# This code is contributed by Neelam Yadav


C#
// C# program for implementation
// of Ford Fulkerson algorithm
using System;
using System.Collections.Generic;
 
public class MaxFlow {
    static readonly int V = 6; // Number of vertices in
                               // graph
 
    /* Returns true if there is a path
    from source 's' to sink 't' in residual
    graph. Also fills parent[] to store the
    path */
    bool bfs(int[, ] rGraph, int s, int t, int[] parent)
    {
        // Create a visited array and mark
        // all vertices as not visited
        bool[] visited = new bool[V];
        for (int i = 0; i < V; ++i)
            visited[i] = false;
 
        // Create a queue, enqueue source vertex and mark
        // source vertex as visited
        List queue = new List();
        queue.Add(s);
        visited[s] = true;
        parent[s] = -1;
 
        // Standard BFS Loop
        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 we find a connection to the sink
                    // node, then there is no point in BFS
                    // anymore We just have to set its parent
                    // and can return true
                    if (v == t) {
                        parent[v] = u;
                        return true;
                    }
                    queue.Add(v);
                    parent[v] = u;
                    visited[v] = true;
                }
            }
        }
 
        // We didn't reach sink in BFS starting from source,
        // so return false
        return false;
    }
 
    // Returns the maximum flow
    // from s to t in the given graph
    int fordFulkerson(int[, ] graph, int s, int t)
    {
        int u, v;
 
        // Create a residual graph and fill
        // the residual graph with given
        // capacities in the original graph as
        // residual capacities in residual graph
 
        // Residual graph where rGraph[i,j]
        // indicates residual capacity of
        // edge from i to j (if there is an
        // edge. If rGraph[i,j] is 0, then
        // there is not)
        int[, ] rGraph = new int[V, V];
 
        for (u = 0; u < V; u++)
            for (v = 0; v < V; v++)
                rGraph[u, v] = graph[u, v];
 
        // This array is filled by BFS and to store path
        int[] parent = new int[V];
 
        int max_flow = 0; // There is no flow initially
 
        // Augment the flow while there is path from source
        // to sink
        while (bfs(rGraph, s, t, parent)) {
            // Find minimum residual capacity of the edhes
            // along the path filled by BFS. Or we can say
            // find the maximum flow through the path found.
            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]);
            }
 
            // update residual capacities of the edges and
            // reverse edges along the path
            for (v = t; v != s; v = parent[v]) {
                u = parent[v];
                rGraph[u, v] -= path_flow;
                rGraph[v, u] += path_flow;
            }
 
            // Add path flow to overall flow
            max_flow += path_flow;
        }
 
        // Return the overall flow
        return max_flow;
    }
 
    // Driver code
    public static void Main()
    {
        // Let us create a graph shown in the above example
        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));
    }
}
 
/* This code contributed by PrinciRaj1992 */


Javascript


输出:

The maximum possible flow is 23

Ford Fulkerson 算法的上述实现称为Edmonds-Karp 算法。 Edmonds-Karp 的想法是在 Ford Fulkerson 实现中使用 BFS,因为 BFS 总是选择一条边数最少的路径。当使用 BFS 时,最坏情况的时间复杂度可以降低到 O(VE 2 )。上述实现使用邻接矩阵表示,尽管 BFS 需要 O(V 2 ) 时间,但上述实现的时间复杂度为 O(EV 3 )(有关时间复杂度的证明,请参阅 CLRS 书籍)
这是一个重要的问题,因为它出现在许多实际情况中。示例包括在给定的流量限制下最大化传输,最大化计算机网络中的数据包流。
Dinc 的 Max-Flow 算法。

锻炼:
修改上述实现,使其在 O(VE 2 ) 时间内运行。