我们给了一个数字 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 )空间,使用动态编程;但我不知道如何。
答案 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+C2
和C2+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, 5
和x=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