繁体   English   中英

计算从列表中选择唯一元素的方法的数量

[英]Counting number of ways to select unique elements from list of lists

我遇到了SPOJ问题423:分配

这个问题要求我计算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(含0和n-1)之间的每个数字都是一次。 对于第一个示例输入,这是微不足道的,是3! = 6 3! = 6 但是在第二个示例中,我很难找到一种方法来计算有效选择的数量。 对于第二个输入,他们给出的答案为7588,但我不知道该如何获得答案。

我已经尝试过用蛮力方法找到[0,...,n-1]的所有排列,并试图根据设置的成员资格查看它们是否是有效组合,但是它太慢了,当我将它真正崩溃时,我的计算机崩溃了试图通过11! = 39916800迭代11! = 39916800 11! = 39916800排列。 因此,我需要做的是找到一种更有效的计数方法。

到目前为止,这是我的代码。 它当前所做的只是将来自stdin的输入解析为称为首选项的列表列表,并将其打印到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()

我有什么方法可以有效地进行计算? 欢迎任何提示/技巧。 我不希望有人为我解决问题。 我只是对应该如何处理感到困惑。

我看到我在2005年解决了这个问题-它仍然是那里最快的第十名,但是使用的内存比其他公认的解决方案少得多。 看看今天的代码,我不知道它在做什么-大声笑;-)

如果我没记错的话,这是“带有禁止位置的排列”的一个实例。 您可以将其与“ rook多项式”一起使用。 归结为计算0-1矩阵的“永久”(另一个搜索词),这是一项计算量巨大的任务。 这就是为什么您没有在SPOJ上看到针对此问题的Python解决方案的原因(我用C语言编写了我的)。

因此,这里没有答案,但是有很多研究要做;-)获得一个公认的程序更多的是学习数学,而不是聪明的编程。

一个提示:在我的笔记中,我看到通过包含全1的特殊输入框节省了很多运行时间。 在那种情况下,结果只是N的阶乘(对于N×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))

这是一个简单的实现(使用第二个示例矩阵):

>>> 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

问题是要计算图中最大二分匹配的总数。

以下摘录自Wikipedia文章可能会有所帮助

图中的匹配数称为图的Hosoya索引。 计算该数量是#P-complete。 它仍然

在特殊情况下,对给定二部图中的完全匹配数进行计数时,P-complete是因为计算永久性

任意0-1矩阵(另一个#P完全问题)的计算与计算以给定矩阵为其二重性矩阵的二部图中的完全匹配数相同。 但是,存在一种用于计算二分匹配数的完全多项式时间随机逼近方案。[10] Kasteleyn的一个非凡定理指出,可以通过FKT算法精确地在多项式时间内计算平面图中完美匹配的数量。

完整图Kn(偶数为n)中完美匹配的数量由双阶因数(n − 1)给出。[11] 电话图中给出了完整图形中的匹配数,但并不限制匹配的完全性。[12]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM