在一组学生可以连续坐多少的方式,来自同一班级的学生必须交替

时间:2013-03-29 13:44:11

标签: algorithm permutation

来自M个班级的N名学生,A[i]是来自class_isum(A[i]) == M的学生人数。所有这些学生将坐在M个座位上,并且没有2个同一班级的学生坐在一起。

这些M学生可以连续坐多少个有效的方法?

例如, 如果N = 2,A = {1,2}, 输出应为2;

如果N = 2,A = {1,3}, 输出应为0;

如果N = 3,A = {1,2,3}, 输出应为120。

技术规格: N< 47; A [i]< 47; 总和(A)< 447;

如果输出大于1000000007,则输出(结果%1000000007)。

3 个答案:

答案 0 :(得分:0)

此解决方案可能不是最佳解决方案,但我认为这是一个良好的开端。

此问题有两个组成部分:

  1. 将每个座位标记为班级(X路)
  2. 为学生分配座位(Y路)
  3. 最终答案等于X * Y.

    第2部分非常简单。 Y = A(1)! A(2)! ...... * A(N)!

    计算第一部分虽然很棘手。您可以使用DP来解决它。复杂性= N * A(1) A(2) ... * A(N)(这对我来说太贵了)

    DP问题是:

    F(a1,a2,..,an,last_letter = 1)= F(a1-1,a2,..,an,last_letter!= 1)+ F(a1,a2-1,..,an ,last_letter!= 1)+ ... + F(A1,A2,...,AN-1,last_letter!= 1)

答案 1 :(得分:0)

考虑以下python代码:

import math

mem={}

def get_id(A, forbidden):
    count = {}
    for a in A:
        if a<>forbidden:
            n = A[a]
            count[n] = count.get(n,0)+1
    return frozenset( [A.get(forbidden, 0)] + count.items() )

def class_seatings(A, forbidden=None):
    if sum(A.values())==1:
        if forbidden in A:
            return 0
        else:
            return 1
    ssum = 0
    for a in A:
        if a <> forbidden:
            n = A[a]
            A2 = dict(A) 
            if n==1:
                del A2[a]
            else:
                A2[a] -= 1
            id = get_id(A2, a)
            if id not in mem:
                mem[id] = class_seatings(A2, a)
            cs = mem[id]
            ssum += cs
    return ssum

def student_seatings(A):
    assert all(map(lambda x: x>0, A.values()))
    facts = map(lambda x: math.factorial(x), A.values())
    mult = reduce(lambda x,y: x*y, facts, 1)
    return mult*class_seatings(A) % 1000000007

看起来基本情况正确:

>>> student_seatings( {1:1, 2:2} )
2
>>> student_seatings( {1:1, 2:2, 3:3} )
120
>>> student_seatings( {1:1, 2:3} )
0
>>> student_seatings( {1:2, 2:2} )
8

然而,在你提到的要求之前,使用memget_id的基本记忆方案开始崩溃。要看到这一点,请观看进展

mem={}
for upper in range(2,11):
    A = dict( (x,x) for x in range(1,upper) )   
    print len(A), student_seatings(A)
    print len(mem)

产生

1 1
0
2 2
4
3 120
20
4 309312
83
5 579005048
329
6 462179000
1286
7 481882817
5004
8 678263090
19447
9 992777307
75581

有人关心改善吗?

答案 2 :(得分:0)

首先请注意,如果输入列表A的有效座位安排为sum(A)=n,则当新的m类到达时,可以轻松计算有效座位安排的数量:

  • m名新学生适应所有可能n+1个有效职位(n名有学生,n+1有效职位。这是组合的确切定义,并且有C(n+1,m)个这样的组合。
  • 然后将m名新生置换,以获得所有可能的有效座位安排。

因此,新类型m的到来使有效座位安排的数量乘以C(n+1,m) * m!。这表明以下算法(在Python中):

import math
import scipy.misc

def f(A) :
    if len(A) == 2 :
        a,b = A
        if a == b  : 
        # two solutions o+o+ and +o+o without order consideration, then multiply by 2! * 2! to get all possible orders within classes
            return math.factorial(a) * math.factorial(b) * 2 
        elif abs( a - b ) == 1 : 
        # o+o+o without order consideration, then multiply by 2! * 3! to get all possible orders within classes
            return math.factorial(a) * math.factorial(b)
        else : # no solution
            return 0
    else : # the number of valid arrangement is multiplied by C(n+1,m) * m!
        return sum( f(A[:i]+A[i+1:]) * scipy.misc.comb( sum(A)-ai + 1, ai ) * math.factorial(ai) for i, ai in enumerate(A) )

让我们检查一下我们得到的结果与OP的例子相同:

f( [ 1,2 ] )
2

f( [ 1,3 ] )
0

f( [ 1, 2, 3 ] )
120.0

有效!让我们试试30个学生的三个班级:

f( [ 30, 30, 30 ] )
2.6058794190003256e+115 # this is greater than the number of baryons in the universe!