Given a flow network represented as a directed graph with V vertices and E edges, where edges[i] = [u, v, w] denotes a directed edge from vertex u to vertex v with capacity w, find the maximum flow that can be sent from the source vertex 1 to the sink vertex V.
The flow through an edge cannot exceed its capacity. The maximum flow is the largest amount of flow that can be transferred from the source to the sink while satisfying all capacity constraints.
The flow can be sent through the following paths: 1 -> 2 -> 4 carrying min(8, 2) = 2 units of flow. 1 -> 3 -> 4 carrying min(10, 3) = 3 units of flow. Therefore, the maximum flow from vertex 1 to vertex 4 is: 2 + 3 = 5.
The flow can be sent through the following paths: 1 -> 2 -> 5 carrying 2 units of flow. 1 -> 2 -> 4 -> 5 carrying 3 units of flow. 1 -> 3 -> 4 -> 5 carrying 2 units of flow. Therefore, the maximum flow from vertex 1 to vertex 5 is: 2 + 3 + 2 = 7.
The idea is to repeatedly find a path from the source S to the sink T and send the maximum possible flow through it.
The flow that can be sent along a path is limited by the edge with the minimum remaining capacity on that path, known as the bottleneck edge.
However, this approach has a major drawback. Once flow is assigned to a path, it cannot be modified later. As a result, an early path selection may block better flow distributions and prevent the algorithm from finding the true maximum flow.
Consider the following flow network:
The actual maximum flow is 20, achieved by sending 10 units through S -> A -> T and 10 units through S -> B -> T.
If the first path chosen is S -> A -> B -> T, the algorithm sends 10 units of flow and gets stuck with a total flow of 10, which is not optimal.
To overcome this limitation, the Ford-Fulkerson Algorithm uses a residual graph and reverse edges, allowing previously assigned flow to be adjusted and rerouted when a better path is found.
Ford-Fulkerson Algorithm - O(V^3 x E) time and O(V^2) space
The Ford-Fulkerson Algorithm repeatedly finds a path from the source to the sink through which additional flow can be sent. Such a path is called an augmenting path.
In this implementation, BFS is used to find an augmenting path in the residual graph.
Step 1: Start with initial flow as 0.
Step 2: While there exists an augmenting path from the source to the sink: Â
Find an augmenting path using any path-finding algorithm, such as BFS or DFS. We have used BFS here and this implementation is called Edmonds-Karp Algorithm.
Find 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.
Step 3: Return the maximum flow.
The minimum residual capacity on an augmenting path is called the bottleneck capacity.
What is a Residual Graph?
The residual graph represents the remaining capacities after some flow has already been sent through the network. Whenever f units of flow are sent through an edge (u, v):
residual[u][v] -= f;
residual[v][u] += f;
Here:
residual[u][v] represents the remaining capacity of the forward edge.
residual[v][u] represents the capacity of the reverse edge.
The reverse capacity allows previously sent flow to be cancelled and redirected through another path when required.
What is a Reverse Edge?
A reverse edge is not an actual edge in the original graph. It is created in the residual graph whenever flow is sent through an edge.
Suppose an edge has capacity: A ----10----> B
If 6 units of flow are sent through it: A ----6/10----> B
Then the residual graph becomes: A ----4----> B and A <----6---- B
Here: A -> B = 4 represents the remaining capacity and B -> A = 6 is a reverse edge.
The reverse edge means: Up to 6 units of the previously assigned flow on A -> B can be canceled and rerouted through another path if required. Without reverse edges, flow decisions would become permanent, and the algorithm might miss the optimal solution.
We use reverse edges to update the residual capacities. When we send flow along a path, we subtract that flow from the forward edges and add the same amount to their reverse edges. T
he reverse capacity represents how much of the previously sent flow can later be cancelled or rerouted if a better augmenting path is found.
Example:
Consider the following flow network:
The maximum possible flow is: 20
Iteration 1:
Suppose BFS finds the path: S -> A -> B -> T
The bottleneck capacity is: min(10, 10, 10) = 10, Send 10 units of flow.
Current flow: maxFlow = 10
Residual Graph After Iteration 1
Notice the reverse edge: B -> A = 10, which means the 10 units of flow currently passing through A -> B can be canceled if a better route is found.
Iteration 2:
The algorithm searches the residual graph and finds: S -> B -> A -> T, Here: B -> A is a reverse edge.
The bottleneck capacity is: min(10, 10, 10) = 10, Send 10 units of flow.
Using the reverse edge does not create new flow. Instead, it: Cancels the previous flow on: A -> B. Redirects that flow through: A -> T
As a result: maxFlow = 20
C++
#include<bits/stdc++.h>usingnamespacestd;// Finds an augmenting path from source to sink in the residual graphboolbfs(vector<vector<int>>&rGraph,intsrc,intsink,vector<int>&parent){intV=rGraph.size();vector<bool>vis(V,false);queue<int>q;q.push(src);vis[src]=true;parent[src]=-1;while(!q.empty()){intu=q.front();q.pop();// Explore all adjacent verticesfor(intv=1;v<V;v++){// Visit only unvisited vertices having positive residual capacityif(!vis[v]&&rGraph[u][v]>0){vis[v]=true;parent[v]=u;// Sink reached, augmenting path foundif(v==sink)returntrue;q.push(v);}}}returnfalse;}// Returns the maximum flow from source (1) to sink (V)intfindMaxFlow(intV,vector<vector<int>>&edges){// Capacity graphvector<vector<int>>graph(V+1,vector<int>(V+1,0));for(auto&edge:edges){intu=edge[0];intv=edge[1];intcap=edge[2];graph[u][v]=cap;}// Residual graph initially equals the capacity graphvector<vector<int>>rGraph=graph;intsrc=1;intsink=V;vector<int>parent(V+1);intmaxFlow=0;// Keep finding augmenting pathswhile(bfs(rGraph,src,sink,parent)){intpathFlow=INT_MAX;// Find bottleneck capacity of the current pathfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];pathFlow=min(pathFlow,rGraph[u][v]);}// Update residual capacities of forward and reverse edgesfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];rGraph[u][v]-=pathFlow;rGraph[v][u]+=pathFlow;}// Add path flow to the total maximum flowmaxFlow+=pathFlow;}returnmaxFlow;}intmain(){intV=4;vector<vector<int>>edges={{1,2,10},{1,3,10},{2,3,10},{2,4,10},{3,4,10}};cout<<findMaxFlow(V,edges)<<endl;return0;}
Java
importjava.util.*;publicclassGFG{// Finds an augmenting path from source to sink in the residual graphstaticbooleanbfs(int[][]rGraph,intsrc,intsink,int[]parent){intV=rGraph.length;boolean[]vis=newboolean[V];Queue<Integer>q=newLinkedList<>();q.offer(src);vis[src]=true;parent[src]=-1;while(!q.isEmpty()){intu=q.poll();// Explore all adjacent verticesfor(intv=1;v<V;v++){// Visit only unvisited vertices having positive residual capacityif(!vis[v]&&rGraph[u][v]>0){vis[v]=true;parent[v]=u;// Sink reached, augmenting path foundif(v==sink)returntrue;q.offer(v);}}}returnfalse;}// Returns the maximum flow from source (1) to sink (V)staticintfindMaxFlow(intV,int[][]edges){// Capacity graphint[][]graph=newint[V+1][V+1];for(int[]edge:edges){intu=edge[0];intv=edge[1];intcap=edge[2];graph[u][v]=cap;}// Residual graph initially equals the capacity graphint[][]rGraph=newint[V+1][V+1];for(inti=0;i<=V;i++)rGraph[i]=graph[i].clone();intsrc=1;intsink=V;int[]parent=newint[V+1];intmaxFlow=0;// Keep finding augmenting pathswhile(bfs(rGraph,src,sink,parent)){intpathFlow=Integer.MAX_VALUE;// Find bottleneck capacity of the current pathfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];pathFlow=Math.min(pathFlow,rGraph[u][v]);}// Update residual capacities of forward and reverse edgesfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];rGraph[u][v]-=pathFlow;rGraph[v][u]+=pathFlow;}// Add path flow to the total maximum flowmaxFlow+=pathFlow;}returnmaxFlow;}publicstaticvoidmain(String[]args){intV=4;int[][]edges={{1,2,10},{1,3,10},{2,3,10},{2,4,10},{3,4,10}};System.out.println(findMaxFlow(V,edges));}}
Python
fromcollectionsimportdequeimportsys# Finds an augmenting path from source to sink in the residual graphdefbfs(rGraph,src,sink,parent):V=len(rGraph)vis=[False]*Vq=deque()q.append(src)vis[src]=Trueparent[src]=-1whileq:u=q.popleft()# Explore all adjacent verticesforvinrange(1,V):# Visit only unvisited vertices having positive residual capacityifnotvis[v]andrGraph[u][v]>0:vis[v]=Trueparent[v]=u# Sink reached, augmenting path foundifv==sink:returnTrueq.append(v)returnFalse# Returns the maximum flow from source (1) to sink (V)deffindMaxFlow(V,edges):# Capacity graphgraph=[[0]*(V+1)for_inrange(V+1)]foredgeinedges:u=edge[0]v=edge[1]cap=edge[2]graph[u][v]=cap# Residual graph initially equals the capacity graphrGraph=[row[:]forrowingraph]src=1sink=Vparent=[0]*(V+1)maxFlow=0# Keep finding augmenting pathswhilebfs(rGraph,src,sink,parent):pathFlow=sys.maxsize# Find bottleneck capacity of the current pathv=sinkwhilev!=src:u=parent[v]pathFlow=min(pathFlow,rGraph[u][v])v=parent[v]# Update residual capacities of forward and reverse edgesv=sinkwhilev!=src:u=parent[v]rGraph[u][v]-=pathFlowrGraph[v][u]+=pathFlowv=parent[v]# Add path flow to the total maximum flowmaxFlow+=pathFlowreturnmaxFlowV=4edges=[[1,2,10],[1,3,10],[2,3,10],[2,4,10],[3,4,10]]print(findMaxFlow(V,edges))
C#
usingSystem;usingSystem.Collections.Generic;classGFG{// Finds an augmenting path from source to sink in the residual graphstaticboolbfs(int[,]rGraph,intsrc,intsink,int[]parent){intV=rGraph.GetLength(0);bool[]vis=newbool[V];Queue<int>q=newQueue<int>();q.Enqueue(src);vis[src]=true;parent[src]=-1;while(q.Count>0){intu=q.Dequeue();// Explore all adjacent verticesfor(intv=1;v<V;v++){// Visit only unvisited vertices having positive residual capacityif(!vis[v]&&rGraph[u,v]>0){vis[v]=true;parent[v]=u;// Sink reached, augmenting path foundif(v==sink)returntrue;q.Enqueue(v);}}}returnfalse;}// Returns the maximum flow from source (1) to sink (V)staticintfindMaxFlow(intV,int[,]edges){// Capacity graphint[,]graph=newint[V+1,V+1];for(inti=0;i<edges.GetLength(0);i++){intu=edges[i,0];intv=edges[i,1];intcap=edges[i,2];graph[u,v]=cap;}// Residual graph initially equals the capacity graphint[,]rGraph=(int[,])graph.Clone();intsrc=1;intsink=V;int[]parent=newint[V+1];intmaxFlow=0;// Keep finding augmenting pathswhile(bfs(rGraph,src,sink,parent)){intpathFlow=int.MaxValue;// Find bottleneck capacity of the current pathfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];pathFlow=Math.Min(pathFlow,rGraph[u,v]);}// Update residual capacities of forward and reverse edgesfor(intv=sink;v!=src;v=parent[v]){intu=parent[v];rGraph[u,v]-=pathFlow;rGraph[v,u]+=pathFlow;}// Add path flow to the total maximum flowmaxFlow+=pathFlow;}returnmaxFlow;}staticvoidMain(){intV=4;int[,]edges={{1,2,10},{1,3,10},{2,3,10},{2,4,10},{3,4,10}};Console.WriteLine(findMaxFlow(V,edges));}}
JavaScript
// Finds an augmenting path from source to sink in the residual graphfunctionbfs(rGraph,src,sink,parent){constV=rGraph.length;constvis=newArray(V).fill(false);constq=[];q.push(src);vis[src]=true;parent[src]=-1;while(q.length>0){constu=q.shift();// Explore all adjacent verticesfor(letv=1;v<V;v++){// Visit only unvisited vertices having positive residual capacityif(!vis[v]&&rGraph[u][v]>0){vis[v]=true;parent[v]=u;// Sink reached, augmenting path foundif(v===sink)returntrue;q.push(v);}}}returnfalse;}// Returns the maximum flow from source (1) to sink (V)functionfindMaxFlow(V,edges){// Capacity graphconstgraph=Array.from({length:V+1},()=>Array(V+1).fill(0));for(constedgeofedges){constu=edge[0];constv=edge[1];constcap=edge[2];graph[u][v]=cap;}// Residual graph initially equals the capacity graphconstrGraph=graph.map(row=>[...row]);constsrc=1;constsink=V;constparent=newArray(V+1);letmaxFlow=0;// Keep finding augmenting pathswhile(bfs(rGraph,src,sink,parent)){letpathFlow=Number.MAX_SAFE_INTEGER;// Find bottleneck capacity of the current pathfor(letv=sink;v!==src;v=parent[v]){constu=parent[v];pathFlow=Math.min(pathFlow,rGraph[u][v]);}// Update residual capacities of forward and reverse edgesfor(letv=sink;v!==src;v=parent[v]){constu=parent[v];rGraph[u][v]-=pathFlow;rGraph[v][u]+=pathFlow;}// Add path flow to the total maximum flowmaxFlow+=pathFlow;}returnmaxFlow;}constV=4;constedges=[[1,2,10],[1,3,10],[2,3,10],[2,4,10],[3,4,10]];console.log(findMaxFlow(V,edges));
Output
20
Note: Note: Using BFS to select augmenting paths makes this implementation the Edmonds-Karp variant of the Ford-Fulkerson method.