In algorithms, many problems have different variations. The maximum flow problem, that you are already familiar with, is no exception. One of them is the minimum cost maximum flow problem. Imagine that you need to choose an optimal route for distributing some goods. You have several routes that allow the transportation of the same amount of produce, which is also the maximal one possible. However, some of the options are not free, so, naturally, you want to minimize the expenses. This is exactly the moment when the minimum cost maximum flow problem arises. So, how can a maximum flow of minimum cost be found? Obviously, this task, just like any other, can be approached in many different ways. Let's discuss one of them, which is the cycle-cancelling algorithm.
Algorithm
First of all, let's remember some definitions, that are crucial for understanding the algorithm.
For starters, a flow network is a directed graph, which has a source and a sink, each edge of which has flow and capacity. Secondly, the flow of the edge is the amount of source that currently goes through it. For example, in a network, representing a pipe system, a flow is the amount of water that goes through the pipe. Additionally, in the algorithm, a residual graph is used. It is a graph, each edge of which has a residual capacity assigned to it. Don't forget, that the residual capacity is the difference between the capacity and the flow of an edge.
Last but not least is a negative cycle. It can be found in weighted graphs. Basically, it is a cycle, formed by the edges, the sum of weights assigned to which is negative. It may sound difficult but becomes much easier after looking at the example.
Imagine, that you have the following weighted graph:
The cycle is negative, as . One more important detail is that a negative cycle and a cycle with a negative edge are different things. For example, in the graph mentioned above, there is a cycle , which is not negative, nevertheless, it has a negative edge in it.
Let's define some additional variables:
and are the vertices of the network;
is the flow of the edge from to ;
is the cost of going through the edge from to . In terms of our example about transporting the produce, it is the amount of money you pay for using a road;
is the capacity of the edge from to ;
Now, we are ready to move on to the algorithm. It is the following:
For each backward edge from to define capacity , flow (skew symmetry), cost .
The initial flow for each edge equals 0.
Find any maximum flow in a network.
Create a weighted graph in the following way: take the initial network and assign a cost to each edge, which residual capacity is positive. The important thing here is to remember about the backward edges. If the residual capacity of such an edge is positive, you need to assign a negative cost to the corresponding edge of the graph (just like it was stated in the step 1 of the algorithm). In this graph you don't need flows and capacities, only the newly assigned costs. Keep in mind that you need to know the cost of going through each edge before the start of the algorithm in order to be able to assign it during this step.
Look for a negative cycle in a graph . If there is none, the algorithm is finished.
If there is a negative cycle, you eliminate it by pushing the maximum flow possible through the cycle. The maximum possible flow in this case equals the minimum residual capacity in the cycle. Pushing the flow here means increasing the flow of each edge of the cycle by a certain amount
Go back to step 5 and repeat the process, until no negative cycle is left.
Take a look at the pseudocode, if too many words bother you:
class CycleCancelling is
network = initNetwork()
maximumFlowNetwork = maximumFlow(network)
method findMinCost() is
costsGraph = buildCostsGraph()
cycle = findNegativeCycle(costsGraph)
while (cycle is not Empty):
flowToPush = min(edge.residualCapacity | edge ∈ cycle)
for (edge ∈ cycle):
edge.flow += flowToPush
costsGraph = buildCostsGraph()
cycle = findNegativeCycle(costsGraph)
method initNetwork() is ... // creates the structure, describing the initial flow network
method maximumFlow() is ... // finds any combination of paths, that in total give the maximum flow in the network
method buildCostsGraph() is ... // creates a weighted graph, described in step 4 of the algorithm
Example
In order to understand the work of the algorithm better, let's perform it ourselves. The network is the following, with capacities assigned to each edge:
The costs of going through each edge — you can think of it as paying for using a road — are the following:
First of all, you need to find any combination of paths that in total give the maximum flow in a network. For example, it may look like this:
Now, let's perform the steps of the algorithm. In each picture, you will see a residual graph on the left and a graph with costs on the right. After performing steps 3 (finding the maximum flow) and 4 (creating a weighted graph with costs) of the algorithm, the graphs look like this:
Now, as step 5 states, we need to try to find a negative cycle. If you look at the graph on the right above, you can see it – a cycle . At step 6 we need to get rid of this cycle by pushing the maximum possible flow through it. Look at the residual graph on the left. The maximum flow equals the minimum residual capacity of the edges of this cycle, which is . After pushing the flow, the graphs look like this:
Now, let's go back to step 5 of the algorithm and find a negative cycle . At step 6 you push the maximum flow possible through it. It equals . After pushing the flow, the graphs look like this:
Once again, we need to look for a negative cycle in the graph on the right. Hooray, finally there are none left!
The final flow of a network is the following:
Note that for the maximum flow, that we found in the beginning, the total cost was 171, but right now it is much less — 101. You can think of it as units of money, that we were able to save after redistributing the flow. It is quite pleasant, isn't it?
Correctness of the algorithm
After learning about the algorithm, you may have a question: does it actually find the optimal paths? Let's check it.
First of all, may there be negative cycles in a graph if the cost of the flow is minimal? Imagine, that there may be. Then you can push the flow through the cycle — the total flow in a network will remain the same, but the cost will decrease. It means, that the flow wasn't optimal in terms of cost from the beginning, therefore, if cost is minimal, there may be no negative cycles.
On the other hand, if there are no negative cycles, does it guarantee that the cost of the flow is the minimum one possible? Let be such a maximum flow, that there are no negative cycles in a graph of costs, and — a maximum flow of minimum cost. It is obvious, that . First off, let's find the difference between the flow in and for each edge of the network. It can be decomposed into cycles, which means that . As we suppose, that there are no cycles of negative cost, then . Previously we've already discovered that , which means that . Therefore, is indeed the maximum flow of minimum cost.
Complexity
By now you are probably eager to implement the algorithm and try it out in some real-life problems. However, before doing that, you need to understand its time complexity.
Let be the maximum capacity of the edges of a network, — the maximum cost of the edges of a network, and — the number of edges. Then the total cost of the flow in the beginning of the work of the algorithm is no bigger than . In a worst-case scenario, on each iteration, you decrease the total cost by 1 unit after finding a negative cycle. As the cost cannot be negative, this leads us to at most iterations. As for finding a negative cycle, you can use Bellman-Ford algorithm, the complexity of which is , where is the number of vertices in a network. Therefore, the total complexity of the cycle-cancelling algorithm is .
Conclusion
To sum up, let's go through the main points of the topic:
The cycle-cancelling algorithm is used for finding the maximum flow of minimum cost.
A negative cycle is a cycle, formed by the edges, the sum of weights assigned to which is negative.
On each step the algorithm creates a graph of costs for the edges with positive residual capacities, and checks if it has any negative cycles. If it does, the maximum possible flow is pushed through the cycle in order to get rid of the negative one.
The complexity of the cycle-cancelling algorithm is , where and are the number of the edges and the vertices in a network respectively, — the maximum capacity of the edges and — the maximum cost of going through the edge.