我们得到了一个数字列表,其中每个数字要么单独出现,要么重复出现。现在的任务是找到排列的总数,我们可以在其中排列此数字列表,这样列表上的两个相邻元素都不相同。关键是要有效地做到这一点,这就是为什么我要寻找一种使用动态编程的算法。长度可能非常大,最多5000个元素,这就是为什么生成所有案例并检查案例是否正确的原因不是一个好主意。我们只需要列表的总排列数,而不是列表的实际排列数。
示例:[1,1,2,2]
输出:2
[1,2,1,2]和[2,1,2,1]
示例:[1,2,3,3]
输出:6
[3,1,2,3],[3,2,1,3],[1,3,2,3],[2,3,1,3],[3,1,3,2 ]和[3,2,3,1]
示例:[1,1,2,2,3,3]
输出:30
可以使用inclusion-exclusion principle解决此问题
如果单个项目的数量是s
,而双重项目对的数量是d
,那么排列的总数是
A0 = (s + 2 * d)! / 2^d
现在,我们必须减一对相邻的排列数。有
P1 = (s+2*d-1)! / 2^(d-1)
每个双精度元素的排列,和
A1 = d *(s+2*d-1)! / 2^(d-1)
所有双精度元素的排列
现在我们必须add两对相邻的排列数。有
P2 = (s+2*d-2)! / 2^(d-2)
每对双精度元素的排列,以及
A2 = C(d,2) * (s+2*d-2)! / 2^(d-2)
对所有可能的双精度元素对的排列(其中C(n,k)
是二项式系数,组合数)。
继续此过程,更改符号,所以>
A(k){k=1..d} = (-1)^k * C(d, k) * (s+2*d-k)! / 2^(d-k)
并对该序列求和以得到最终结果
最后一个例子(s=0,d=3
):
6!/2^3 - 3*5!/2^2 + 3*4!/2 - 3! =
720/8 - 360/4 + 72/2 - 6 =
90 - 90 + 36 - 6 = 30