Algorithm 一组学生可以以多少种方式坐成一排,同一班级的学生必须轮流坐
有来自Algorithm 一组学生可以以多少种方式坐成一排,同一班级的学生必须轮流坐,algorithm,permutation,Algorithm,Permutation,有来自N班级的M学生,A[i]是来自i班级的学生人数,sum(A[i])==M。所有这些学生将排成一排,有M个座位,同一班级没有两个学生坐在一起 这些M学生可以通过多少种有效方式坐成一排 比如说,, 如果N=2,A={1,2}, 输出应为2 如果N=2,A={1,3}, 输出应为0 如果N=3,A={1,2,3}, 输出应该是120 技术规格: N
N
班级的M
学生,A[i]
是来自i
班级的学生人数,sum(A[i])==M
。所有这些学生将排成一排,有M
个座位,同一班级没有两个学生坐在一起
这些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
如果输出大于100000007,则大于输出(结果%100000007)。此解决方案可能不是最优的,但我认为这是一个良好的开端 此问题有两个组成部分:
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)+
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
然而,使用mem
和get_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
有人想改进吗?首先请注意,给定输入列表
a
的有效座位安排,sum(a)=n
,当新的m
等级到达时,计算有效座位安排的数量很容易:
- 将
新生安排在所有可能的m
有效位置(有n+1
老学生,这意味着n
有效位置)。这是组合的精确定义,有n+1
这样的组合李>C(n+1,m)
- 然后排列
新生,以获得所有可能的有效座位安排李>m
m
等级的到来将有效座位安排的数量乘以C(n+1,m)*m代码>。这建议使用以下算法(在Python中):
导入数学
导入scipy.misc
定义f(A):
如果len(A)==2:
a、 b=a
如果a==b:
#不考虑顺序的两个解o+o+和+o+o,然后乘以2!*2.在类中获取所有可能的命令
返回数学阶乘(a)*数学阶乘(b)*2
elif abs(a-b)=1:
#o+o+o不考虑顺序,然后乘以2!*3.在类中获取所有可能的命令
返回数学阶乘(a)*数学阶乘(b)
否则:#没有解决方案
返回0
否则:#有效排列数乘以C(n+1,m)*m!
返回和(f(A[:i]+A[i+1:])*scipy.misc.comb(和(A)-ai+1,ai)*枚举(A)中i,ai的数学阶乘(ai))
让我们检查一下,我们得到的结果是否与OP的示例相同:
f([1,2])
2.
f([1,3])
0
f([1,2,3])
120
它起作用了!让我们试着让30名学生参加三个班:
f([30,30,30])
2.605879410003256e+115#这比宇宙中重子的数量还要多!
“同一个班级没有两个学生坐在一起。”只表示“必须交替”表示N==2
如果你不知道什么是排列,可能会帮助你找到你想要的。我不确定这是否属于stackoverflow,而且看起来很像家庭作业,但对一个有趣的问题进行一次向上投票。若其中一个A[i]大于M/2+1,那个么答案是0。。。很难得到正确的公式。正如@Vesper所暗示的,这是一个比看起来复杂得多的问题,因为有些情况下没有解决方案(M=12,N=2,a={10,2}),有些情况下有很多解决方案,甚至可能有唯一解决方案。不可能有一个简单的封闭式公式或算法。
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