Ford-Fulkerson Algorithm


The Ford-Fulkerson Algorithm is a classical algorithm used to compute the maximum flow in a flow network. The algorithm was developed by L.R. Ford Jr. and D.R. Fulkerson in 1956. The idea behind the Ford-Fulkerson algorithm is simple: it repeatedly finds augmenting paths in the residual graph and increases the flow until no more augmenting paths can be found. This approach is key in solving maximum flow problems in various practical scenarios, such as network design, transportation, and resource allocation.

The algorithm is based on the flow network concept, where we have:

  • Nodes (representing sources, sinks, or intermediate points).
  • Edges (representing the capacity of a flow between nodes).

Key Concepts in Flow Networks

  • Source: The starting point where the flow originates.
  • Sink: The endpoint where the flow is directed.
  • Capacity: The maximum possible flow between two nodes.
  • Flow: The amount of material being transported through the network.
  • Residual Graph: The graph that shows the remaining capacity for each edge after flow has been sent through it.

The maximum flow problem is to find the greatest amount of flow that can be sent from the source to the sink without exceeding the capacities of the edges.


Ford-Fulkerson Algorithm Overview

The Ford-Fulkerson algorithm operates in the following steps:

  1. Initialization: Start with zero flow on all edges in the graph.
  2. Find an augmenting path: Search for a path from the source to the sink in the residual graph where the capacity is greater than zero.
  3. Augment the flow: Increase the flow along the found augmenting path by the minimum residual capacity of the path.
  4. Update the residual graph: Adjust the residual capacities of the edges in the found path.
  5. Repeat until there are no more augmenting paths from the source to the sink.

The maximum flow is the total amount of flow from the source to the sink after no more augmenting paths can be found.


Ford-Fulkerson Algorithm in Action

Let's walk through an example of how the Ford-Fulkerson algorithm works.

Example Flow Network:

Consider the following flow network:

          10
    A ----------> B
    |            |
   10           10
    |            |
    v            v
    C ----------> D
         10
  • The graph shows 4 nodes (A, B, C, D) and 4 edges.
  • The capacities of the edges are given as the numbers beside them (e.g., edge from A to B has a capacity of 10).

The goal is to compute the maximum flow from source A to sink D.


Ford-Fulkerson Algorithm Implementation

We'll now look at how to implement the Ford-Fulkerson algorithm using Depth-First Search (DFS) for finding augmenting paths.

1. Ford-Fulkerson in Python

# Python program to implement Ford-Fulkerson algorithm using DFS

class Graph:
    def __init__(self, vertices):
        self.V = vertices  # Number of vertices
        self.graph = [[0] * vertices for _ in range(vertices)]  # Capacity matrix

    def add_edge(self, u, v, capacity):
        self.graph[u][v] = capacity

    def dfs(self, s, t, parent):
        visited = [False] * self.V
        stack = [s]
        visited[s] = True

        while stack:
            u = stack.pop()

            for v in range(self.V):
                if not visited[v] and self.graph[u][v] > 0:
                    stack.append(v)
                    visited[v] = True
                    parent[v] = u

        return visited[t]

    def ford_fulkerson(self, source, sink):
        parent = [-1] * self.V  # Store the path
        max_flow = 0

        while self.dfs(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

            # Update residual capacities
            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


# Sample usage
graph = Graph(4)
graph.add_edge(0, 1, 10)
graph.add_edge(0, 2, 10)
graph.add_edge(1, 2, 5)
graph.add_edge(1, 3, 10)
graph.add_edge(2, 3, 10)

source = 0  # Source node
sink = 3    # Sink node
print(f"The maximum possible flow is {graph.ford_fulkerson(source, sink)}")

Explanation:

  • The Graph class represents a flow network with V vertices.
  • The add_edge function adds edges with their capacities.
  • The dfs function is used to find augmenting paths in the residual graph.
  • The ford_fulkerson function calculates the maximum flow from the source to the sink using DFS to find augmenting paths and updating the residual graph.

In this example, the flow from node A (0) to node D (3) is calculated, resulting in the maximum flow.


2. Ford-Fulkerson in C++

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Graph {
public:
    int V;
    vector<vector<int>> graph;

    Graph(int V) {
        this->V = V;
        graph.resize(V, vector<int>(V, 0));
    }

    void addEdge(int u, int v, int capacity) {
        graph[u][v] = capacity;
    }

    bool dfs(int s, int t, vector<int>& parent) {
        vector<bool> visited(V, false);
        visited[s] = true;
        parent[s] = -1;
        vector<int> stack = {s};

        while (!stack.empty()) {
            int u = stack.back();
            stack.pop_back();

            for (int v = 0; v < V; ++v) {
                if (!visited[v] && graph[u][v] > 0) {
                    stack.push_back(v);
                    visited[v] = true;
                    parent[v] = u;
                }
            }
        }
        return visited[t];
    }

    int fordFulkerson(int source, int sink) {
        vector<int> parent(V);
        int maxFlow = 0;

        while (dfs(source, sink, parent)) {
            int pathFlow = INT_MAX;
            for (int v = sink; v != source; v = parent[v]) {
                int u = parent[v];
                pathFlow = min(pathFlow, graph[u][v]);
            }

            maxFlow += pathFlow;

            for (int v = sink; v != source; v = parent[v]) {
                int u = parent[v];
                graph[u][v] -= pathFlow;
                graph[v][u] += pathFlow;
            }
        }

        return maxFlow;
    }
};

int main() {
    Graph g(4);
    g.addEdge(0, 1, 10);
    g.addEdge(0, 2, 10);
    g.addEdge(1, 2, 5);
    g.addEdge(1, 3, 10);
    g.addEdge(2, 3, 10);

    int source = 0;
    int sink = 3;
    cout << "The maximum possible flow is " << g.fordFulkerson(source, sink) << endl;
    return 0;
}

Explanation:

  • The C++ implementation closely mirrors the Python version.
  • The dfs function finds augmenting paths.
  • The fordFulkerson function calculates the maximum flow and updates the residual graph.

Performance Analysis of the Ford-Fulkerson Algorithm

  • Time Complexity: The time complexity of the Ford-Fulkerson algorithm depends on the number of augmenting paths found. In the worst case, it can be O(max_flow * E), where max_flow is the value of the maximum flow, and E is the number of edges. This is because each DFS can take O(E) time, and the algorithm may require up to max_flow iterations in the worst case.
  • Space Complexity: O(V + E), where V is the number of vertices and E is the number of edges, as the residual graph and parent array need to be stored.

Applications of Ford-Fulkerson Algorithm

  • Network Flow Problems: Used in computer networks to determine the maximum amount of data that can be transmitted from source to destination.
  • Bipartite Matching: Solving the maximum bipartite matching problem in graph theory.
  • Project Scheduling: Used in resource allocation and scheduling problems.
  • Traffic Routing: Finding optimal traffic flow in transportation networks.