将n个相同的球分成小组的方式的数量,使得每个小组至少有k个球?

时间:2016-02-27 12:49:43

标签: algorithm combinations dynamic-programming

我正在尝试使用带有memoization的递归来执行此操作,我已经确定了以下基本情况。

I)当n == k时,只有一个组有所有球。

II)当k> n时,没有任何组可以拥有至少k个球,因此为零。

我无法从这里前进。如何做到这一点?

作为n = 6时的例子,k = 2    (2,2,2)     (4,2)     (3,3)     (6)

即可形成4种不同的分组。

2 个答案:

答案 0 :(得分:3)

这可以用下面描述的二维递归公式表示:

T(0, k) = 1
T(n, k) = 0   n < k, n != 0
T(n, k) = T(n-k, k)                 +           T(n, k + 1)
             ^                                       ^
    There is a box with k balls,        No box with k balls, advance to next k
            put them 

在上文中,T(n,k)n个球的分布数,因此每个框至少得到k
诀窍是将k视为尽可能少的球数,并将问题分成两个场景:是否有一个精确k球的盒子(如果有的话,放置它们)或者用n-k球递归),或者没有(然后用k+1的最小值和相同数量的球递归。)

示例,计算您的示例:T(6,2)(6个球,每盒至少2个):

T(6,2) = T(4,2) + T(6,3) 
T(4,2) = T(2,2) + T(4,3) = T(0,2) + T(2,3) + T(1,3) + T(4,4) =
       = T(0,2) + T(2,3) + T(1,3) + T(0,4) + T(4,5) = 
       =  1     +  0     +  0     +  1     +    0 
       = 2
T(6,3) = T(3,3) + T(6,4) = T(0,3) + T(3,4) + T(2,4) + T(6,5)
       = T(0,3) + T(3,4) + T(2,4) + T(1,5) + T(6,6) = 
       = T(0,3) + T(3,4) + T(2,4) + T(1,5) + T(0,6) + T(6,7) =
       =   1    +   0    +   0    +   0    + 1      + 0 
       = 2
T(6,2) = T(4,2) + T(6,3) = 2 + 2 = 4

使用动态编程,可以在O(n^2)时间内计算。

答案 1 :(得分:0)

这种情况可以很简单地解决:

桶数

最大桶数b可以按如下方式确定:

b = roundDown(n / k)

每个有效的分发版最多可以使用b个桶。

x存储桶的分发数

对于给定数量的桶,可以非常简单地找到分配数量:
向每个桶分发k个球。找出将剩余球(r = n - k * x)分发到x桶的方式的数量:

total_distributions(x) = bincoefficient(x , n - k * x)

编辑:如果订单有问题,这将是onyl工作。由于它没有问题,我们可以在这里使用一些技巧:

每个分布都可以映射到一系列数字。例如:d = {d1 , d2 , ... , dx}。我们可以轻松生成所有这些序列,从&#34; first&#34;序列{r , 0 , ... , 0},然后从左向右移动1。所以下一个序列看起来像这样:{r - 1 , 1 , ... , 0}。如果仅生成匹配d1 >= d2 >= ... >= dx的序列,则不会生成重复项。此约束可以轻松用于优化此搜索:如果给出da,我们只能将1从db移动到a = b - 1(使用da - 1 >= db + 1),因为否则违反了数组的排序约束。移动的1s总是最可移动的。想到这一点的另一种方法是将r视为一元数,然后简单地将该字符串拆分成组,以便每个组至少是它的后继者。

 countSequences(x)
     sequence[]
     sequence[0] = r

     sequenceCount = 1

     while true
         int i = findRightmostMoveable(sequence)

         if i == -1
             return sequenceCount

         sequence[i] -= 1
         sequence[i + 1] -= 1
         sequenceCount

findRightmostMoveable(sequence)
    for i in [length(sequence) - 1 , 0)
       if sequence[i - 1] > sequence[i] + 1
           return i - 1

return -1

如果我们查看序列的结构转换(更准确地说是序列的两个元素之间的差异),实际上findRightmostMoveable可以稍微优化一下。但说实话,我到目前为止还懒得进一步优化。

将各个部分放在一起

range(1 , roundDown(n / k)).map(b -> countSequences(b)).sum()