实现:特殊分配问题的算法

时间:2018-11-21 16:50:10

标签: algorithm

我们给了一个数字 x 和一组 n 个硬币,其面额为 v 1 ,< em> v 2 ,…, v n

硬币必须在爱丽丝和鲍勃之间分配,但每个人的硬币必须加起来至少为 x

例如,如果 x = 1, n = 2,并且 v 1 = v 2 = 2,那么有两种可能的分布:一种是爱丽丝获得硬币1,鲍勃获得硬币2,另一种则相反。 (即使两种硬币面额相同,这些分布也被认为是不同的。)

我有兴趣计算可能的分布。我很确定这可以在 O nx )时间和 O n + x )空间,使用动态编程;但我不知道如何。

3 个答案:

答案 0 :(得分:4)

计算一个人获得少于x的方法,将其加倍,然后从加倍的总数中减去,将集合一分为二(第二种斯特林数{n, 2})。

例如,

{2, 3, 3, 5}, x = 5

i  matrix
0  2: 1
1  3: 1 (adding to 2 is too much)
2  3: 2
3  N/A (≥ x)

3 ways for one person to get
less than 5.

Total ways to partition a set
of 4 items in 2 is {4, 2} = 7

2 * 7 - 2 * 3 = 8

下面的Python代码使用MBo的例程。如果您喜欢此答案,请考虑对该答案进行投票。

# Stirling Algorithm
# Cod3d by EXTR3ME
# https://extr3metech.wordpress.com
def stirling(n,k):
  n1=n
  k1=k
  if n<=0:
    return 1
  elif k<=0:
    return 0   
  elif (n==0 and k==0):
    return -1
  elif n!=0 and n==k:
    return 1
  elif n<k:
    return 0
  else:
    temp1=stirling(n1-1,k1)
    temp1=k1*temp1
    return (k1*(stirling(n1-1,k1)))+stirling(n1-1,k1-1)

def f(coins, x):
  a = [1] + (x-1) * [0]

  # Code by MBo
  # https://stackoverflow.com/a/53418438/2034787
  for c in coins:
      for i in xrange(x - 1, c - 1, -1):
          if a[i - c] > 0:
              a[i] = a[i] + a[i - c]

  return 2 * (stirling(len(coins), 2) - sum(a) + 1)

print f([2,3,3,5], 5) # 8
print f([1,2,3,4,4], 5) # 16

答案 1 :(得分:3)

如果所有硬币的总和为S,则第一人称可获得x..S-x的钱。

使数组A的长度为S-x+1,并用给定硬币填充变化A[i]的变体数量(例如某种硬币找零问题)。

要提供唯一性(不要将C1+C2C2+C1视为两个变体),请反向填充数组

A[0] = 1
for C in Coins:
    for i = S-x downto C:
        if A[i - C] > 0:
             A[i] = A[i] + A[i - C] 
             //we can compose value i as i-C and C

然后将A范围内的x..S-x个条目相加

硬币2, 3, 3, 5x=5的示例。
S = 13, S-x = 8

按顺序使用硬币后的阵列状态:

0 1 2 3 4 5 6 7 8  //idx
1   1
1   1 1   1
1   1 2   2 1   1 
1   1 2   3 1 1 3  

因此,有8种变体来分发这些硬币。快速检查(3'表示第二枚硬币3):

2 3      3' 5
2 3'     3 5
2 3 3'   5  
2 5      3 3'
3 3'     2 5
3 5      2 3'
3' 5     2 3
5        2 3 3'

答案 2 :(得分:3)

您还可以在O(A * x ^ 2)的时间和内存中解决该问题,从而为该dp添加备忘录:

solve(A, pos, sum1, sum2):
    if (pos == A.length) return sum1 == x && sum2 == x
    return solve(A, pos + 1, min(sum1 + A[pos], x), sum2) + 
           solve(A, pos + 1, sum1, min(sum2 + A[pos], x))

print(solve(A, 0, 0, 0))

因此,根据x ^ 2