另一个答案很好,但您实际上并不需要涉及流,因为这也可以简化为普通的最大二分匹配:
- 对于每个平面,将另一个辅助平面添加到平面分区,其边缘与第一个平面相同的飞行员。
- 找到最大二分匹配M。
- 现在当且仅当 M = 2 N 时答案才是正确的。
如果您愿意,您可以将其视为每架飞机需要一名飞行员和一名副驾驶,现在与每架飞机关联的两个顶点代表这两个角色。
最大二分匹配的减少是线性时间,因此使用例如Hopcroft–Karp algorithm找到匹配的,可以解决O(|E| √|V|)里的问题 E 是分区之间的边数,V = T + N。
在实践中,使用基于最大流的方法的改进应该取决于您的实现质量以及图表表示的特定选择,但您可能会更好地使用这种方式。
实现示例
为了说明最后一点,让我们来看看这两种减少在实践中的表现。由于其内置内存局部性而经常有用的图的一种表示是CSR matrix,因此让我们假设输入是这样一个矩阵,其行对应于平面,其列对应于飞行员.
我们将使用Python library SciPy,它带有用于最大二分匹配和最大流量的算法,并且可以与引擎盖下的图的 CSR 矩阵表示一起使用。
在上面给出的算法中,我们需要构造图的邻接矩阵,并添加额外的顶点。这只是将输入矩阵堆叠在其自身之上的结果,这在 CSR 数据结构方面很简单:按照维基百科的表示法,COL_INDEX 应该只是重复,并且 ROW_INDEX 应该替换为与副本连接的 ROW_INDEX ROW_INDEX 的所有元素都增加了 ROW_INDEX 的最后一个元素。
在 SciPy 中,对 OP 中的问题回答是或否的完整实现如下所示:
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
在@HeatherGuarnera 的回答给出的最大流量方法中,我们需要设置新图的完整邻接矩阵。这也相对简单;输入矩阵将显示为邻接矩阵的某个子矩阵,我们需要为源顶点添加一行,为目标添加一列。 SciPy 的最大流量求解器的example section of the documentation 实际上包含了实际情况的说明。采用这个,一个完整的解决方案如下:
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
让我们在由 40 架飞机和 100 名飞行员组成的单个示例上比较两种方法的时序,该图的边密度为 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
基于匹配的方法速度更快,但速度并不快。在更大的问题上,我们将开始看到使用匹配的优势;拥有 400 架飞机和 1000 名飞行员:
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
同样,这种精确的比较依赖于 SciPy 中特定预定义求解器的使用以及它们是如何实现的,但如果没有别的,这表明越简单越好。