4

I have this question :

Airline company has N different planes and T pilots. Every pilot has a list of planes he can fly. Every flight needs 2 pilots. The company want to have as much flights simultaneously as possible. Find an algorithm that finds if you can have all the flights simultaneously.

This is the solution I thought about is finding max flow on this graph: enter image description here

I am just not sure what the capacity should be. Can you help me with that?

fuglede
  • 17,388
  • 2
  • 54
  • 99
Daniel16
  • 113
  • 8
  • Could you explain your approach? – Konstantin Yovkov Jan 27 '20 at 14:39
  • And also, can you share the constraints for N and T? I think I can suggest a dynamic programming solution for this, which might not actually work, if the constraints are quite huge. – Konstantin Yovkov Jan 27 '20 at 14:41
  • My solution is building a bipartite graph that has the pilots in one side and the planes on the other side. The edges between them would connect every pilot to the planes he can fly. The problem is I am not sure about how to define the capacity. There are no constraints on N and T. – Daniel16 Jan 27 '20 at 14:45
  • You assume this problem is well represented as a capacity problem. What makes you certain about this? – trincot Jan 27 '20 at 15:39
  • This is what I think is the solution. I am not sure about it. – Daniel16 Jan 27 '20 at 16:04
  • 2
    Sounds like a special case of Maximum Bipartite Matching where one side should have two out going nodes (A plane needs two pilots). I like this problem. upvoted. – User_67128 Jan 27 '20 at 16:31
  • Since each plane needs 2 pilots, we can't have more than (T/2) flights simultaneously. In that case we can assign (T/2) pilots with same number of planes (starting with the pilot who has less options). Once we have done with it, then we can go for the maximum matching Hopcroft–Karp algorithm. – User_67128 Jan 27 '20 at 17:29
  • The capacity into each "pilot" node ought to be 1 and the capacity out of each "plane" node ought to be 2. But this won't work as a solution to the problem because the flow can also be maximised by assigning each pilot to a different plane, instead of assigning two pilots to each of half of the planes. – kaya3 Jan 27 '20 at 18:56
  • I didn't say that. @kaya3. I said we can assign half pilots to planes (starting with les experience pilots - less options). And then for the remaining pilots (still it is a bipartite graph) we can go for maximum matching using Hopcroft–Karp algorithm. – User_67128 Jan 27 '20 at 20:49
  • @ManojBanik My comment was not a response to yours. I was responding directly to the question, which asks about modelling it as a max-flow problem. – kaya3 Jan 27 '20 at 20:52

2 Answers2

0

Great idea to find the max flow.

  • For each edge from source --> pilot, assign a capacity of 1. Each pilot can only fly one plane at a time since they are running simultaneously.
  • For each edge from pilot --> plane, assign a capacity of 1. If this edge is filled with flow of 1, it represents that the given pilot is flying that plane.
  • For each edge from plane --> sink, assign a capacity of 2. This represents that each plane must be supplied by exactly 2 pilots.

Now, find a maximum flow. If the resulting maximum flow is two times the number of planes, then it's possible to satisfy the constraints. In this case, the edges between planes and pilots that are at capacity represent the matching.

0

The other answer is fine but you don't really need to involve flow as this can be reduced just as well to ordinary maximum bipartite matching:

  • For each plane, add another auxiliary plane to the plane partition with edges to the same pilots as the first plane.
  • Find a maximum bipartite matching M.
  • The answer is now true if and only if M = 2 N.

If you like, you can think of this as saying that each plane needs a pilot and a co-pilot, and the two vertices associated to each plane now represents those two roles.

The reduction to maximum bipartite matching is linear time, so using e.g. the Hopcroft–Karp algorithm to find the matching, you can solve the problem in O(|E| √|V|) where E is the number of edges between the partitions, and V = T + N.

In practice, the improvement over using a maximum flow based approach should depend on the quality of your implementations as well as the particular choice of representation of the graph, but chances are that you're better off this way.

Implementation example

To illustrate the last point, let's give an idea of how the two reductions could look in practice. One representation of a graph that's often useful due to its built-in memory locality is that of a CSR matrix, so let us assume that the input is such a matrix, whose rows correspond to the planes, and whose columns correspond to the pilots.

We will use the Python library SciPy which comes with algorithms for both maximum bipartite matching and maximum flow, and which works with CSR matrix representations for graphs under the hood.

In the algorithm given above, we will then need to construct the biadjacency matrix of the graph with the additional vertices added. This is nothing but the result of stacking the input matrix on top of itself, which is straightforward to phrase in terms of the CSR data structures: Following Wikipedia's notation, COL_INDEX should just be repeated, and ROW_INDEX should be replaced with ROW_INDEX concatenated with a copy of ROW_INDEX in which all elements are increased by the final element of ROW_INDEX.

In SciPy, a complete implementation which answers yes or no to the problem in OP would look as follows:

import numpy as np
from scipy.sparse.csgraph import maximum_bipartite_matching

def reduce_to_max_matching(a):
    i, j = a.shape
    data = np.ones(a.nnz * 2, dtype=bool)
    indices = np.concatenate([a.indices, a.indices])
    indptr = np.concatenate([a.indptr, a.indptr[1:] + a.indptr[-1]])
    graph = csr_matrix((data, indices, indptr), shape=(2*i, j))
    return (maximum_bipartite_matching(graph) != -1).sum() == 2 * i

In the maximum flow approach given by @HeatherGuarnera's answer, we will need to set up the full adjacency matrix of the new graph. This is also relatively straightforward; the input matrix will appear as a certain submatrix of the adjacency matrix, and we need to add a row for the source vertex and a column for the target. The example section of the documentation for SciPy's max flow solver actually contains an illustration of what this looks like in practice. Adopting this, a complete solution looks as follows:

import numpy as np
from scipy.sparse.csgraph import maximum_flow

def reduce_to_max_flow(a):
    i, j = a.shape
    n = a.nnz
    data = np.concatenate([2*np.ones(i, dtype=int), np.ones(n + j, dtype=int)])
    indices = np.concatenate([np.arange(1, i + 1),
                              a.indices + i + 1,
                              np.repeat(i + j + 1, j)])
    indptr = np.concatenate([[0],
                             a.indptr + i,
                             np.arange(n + i + 1, n + i + j + 1),
                             [n + i + j]])
    graph = csr_matrix((data, indices, indptr), shape=(2+i+j, 2+i+j))
    flow = maximum_flow(graph, 0, graph.shape[0]-1)
    return flow.flow_value == 2*i

Let us compare the timings of the two approaches on a single example consisting of 40 planes and 100 pilots, on a graph whose edge density is 0.1:

from scipy.sparse import random
inp = random(40, 100, density=.1, format='csr', dtype=bool)
%timeit reduce_to_max_matching(inp)  # 191 µs ± 3.57 µs per loop
%timeit reduce_to_max_flow(inp)      # 1.29 ms ± 20.1 µs per loop

The matching-based approach is faster, but not by a crazy amount. On larger problems, we'll start to see the advantages of using matching instead; with 400 planes and 1000 pilots:

inp = random(400, 1000, density=.1, format='csr', dtype=bool)
%timeit reduce_to_max_matching(inp)  # 473 µs ± 5.52 µs per loop
%timeit reduce_to_max_flow(inp)      # 68.9 ms ± 555 µs per loop

Again, this exact comparison relies on the use of specific predefined solvers from SciPy and how those are implemented, but if nothing else, this hints that simpler is better.

fuglede
  • 17,388
  • 2
  • 54
  • 99