我遇到了SPOJ Problem 423: Assignments的问题。
问题要求我将n个独特主题的可能分配数量计算给n个独特的学生,以便每个学生都能获得他/她喜欢的一个主题。我想出了一种方法来将输入解析为名为preferences
的列表列表。每个内部列表是一个学生喜欢的主题列表(由0和n-1之间的整数表示)。
例如输入:
1 1 1
1 1 1
1 1 1
我得到[[0, 1, 2], [0, 1, 2], [0, 1, 2]]
。
输入:
1 0 0 1 0 0 0 0 0 1 1
1 1 1 1 1 0 1 0 1 0 0
1 0 0 1 0 0 1 1 0 1 0
1 0 1 1 1 0 1 1 0 1 1
0 1 1 1 0 1 0 0 1 1 1
1 1 1 0 0 1 0 0 0 0 0
0 0 0 0 1 0 1 0 0 0 1
1 0 1 1 0 0 0 0 0 0 1
0 0 1 0 1 1 0 0 0 1 1
1 1 1 0 0 0 1 0 1 0 1
1 0 0 0 1 1 1 1 0 0 0
我得到了列表[[0, 3, 9, 10], [0, 1, 2, 3, 4, 6, 8], [0, 3, 6, 7, 9], [0, 2, 3, 4, 6, 7, 9, 10], [1, 2, 3, 5, 8, 9, 10], [0, 1, 2, 5], [4, 6, 10], [0, 2, 3, 10], [2, 4, 5, 9, 10], [0, 1, 2, 6, 8, 10], [0, 4, 5, 6, 7]]
。
现在我需要做的是计算我可以从每个内部列表中选择唯一编号的方式的数量,这样就不会选择任何元素两次,并且在所有选择中只选择0到n-1之间的每个数字。对于第一个示例输入,这是微不足道的,它是3! = 6
。但在第二个例子中,我很难找到一种方法来计算有效选择的数量。他们给第二个输入的答案为7588,但我不知道如何达到答案。
我已经尝试过使用蛮力方法查找[0,...,n-1]的所有排列并尝试查看它们是否是基于集合成员资格的有效组合,但它太慢了它实际上已经崩溃了当我试图迭代11! = 39916800
排列时计算机。所以我需要做的是找到一种更有效的计算方法。
到目前为止,这是我的代码。它目前所做的只是将stdin的输入解析为名为preferences的列表列表并将其打印到stdout。
def main():
t = int(raw_input())
from itertools import repeat
for _ in repeat(None, t):
n = int(raw_input())
preferences = [[] for _ in repeat(None, n)]
for student in xrange(n):
row = map(int, raw_input().split())
for i in xrange(len(row)):
if row[i] == 1:
preferences[student].append(i)
print preferences
if __name__ == '__main__':
main()
我可以使用任何方法来有效地计算这个数字吗?欢迎任何提示/提示。我不希望有人为我解决问题。我对如何处理这个问题感到困惑。
答案 0 :(得分:3)
我看到我在2005年解决了这个问题 - 它仍然是第10快的,但使用的内存远远少于其他公认的解决方案。看看今天的代码,我不知道它在做什么 - 大声笑; - )
如果我没记错的话,这是“禁止排名的排列”的一个例子。这是一个你可以使用的搜索词,以及“车多项式”。它归结为计算0-1矩阵的“永久”(另一个搜索项),这是一项计算成本高昂的任务。这就是为什么你在SPOJ上看不到这个问题的可接受的Python解决方案(我用C写的)。
所以,那里没有答案,但需要研究很多东西;-)获得一个可接受的程序,更多的是研究数学而不是聪明的编程。
一个提示:在我的笔记中,我看到我通过包含全1的特殊套管输入矩阵保存了 lot 的运行时间。在这种情况下,结果只是N的阶乘(对于N-by-N输入)。祝你好运: - )
这显示了为0-1矩阵实现Ryser公式的相对简单的方法。行被视为普通整数,索引子集也被视为普通整数。可以添加许多微优化(例如,如果prod
变为0,则提前退出循环;如果矩阵完全为1,则返回math.factorial(n)
;等等。
_pc = []
for i in range(256):
c = 0
while i:
# clear last set bit
i &= i-1
c += 1
_pc.append(c)
def popcount(i):
"Return number of bits set."
result = 0
while i:
result += _pc[i & 0xff]
i >>= 8
return result
def perm(a):
"Return permanent of 0-1 matrix. Each row is an int."
result = 0
n = len(a)
for s in range(1 << n):
prod = 1
for row in a:
prod *= popcount(row & s)
if popcount(s) & 1:
result -= prod
else:
result += prod
if n & 1:
result = -result
return result
def matrix2ints(a):
return [int("".join(map(str, row)), 2)
for row in a]
def matrix_perm(a):
"Return permanent of 0-1 matrix."
return perm(matrix2ints(a))
答案 1 :(得分:2)
这是一个天真的实现(使用第二个示例矩阵):
>>> M = [[1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1], [0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1], [1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1], [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]]
>>> L = len(M)
>>> M = [[k for k in xrange(L) if l[k] == 1] for l in M] # Gets the indices of '1's
>>> def count_assignment(taken,row):
if row >= L: return 1
c = 0
for e in M[row]:
if e not in taken: c = c + count_assignment(taken + [e], row + 1)
return c
>>> count_assignment([], 0)
7588
答案 2 :(得分:1)
问题在于计算图表中最大二分匹配的总数。
以下摘录Wikipedia article可能有所帮助
图表中的匹配数称为Hosoya指数 图形。计算此数量是#P-complete。它仍然是
P-complete在计算给定二分图中完美匹配数的特殊情况下,因为计算永久性
任意0-1矩阵的(另一个#P-complete问题)是相同的 计算二分图中完美匹配的数量 具有给定矩阵作为其biadjacency矩阵。但是,那里 存在一个完全多项式时间随机化近似方案 计算二分匹配的数量。[10]一个了不起的定理 Kasteleyn表示平面中完美匹配的数量 可以通过FKT在多项式时间内精确计算图形 算法
完整图形Kn中的完美匹配数(n偶数) 由双因子(n - 1)给出!! [11]的数量 完整图表中的匹配,不限制匹配 完美的,由电话号码给出。[12]