【发布时间】:2021-05-26 10:00:03
【问题描述】:
这个问题类似于硬币找零问题,但有一点不同。
问题表述为:您有一组硬币,并且您知道硬币的价值以及其中每种硬币的数量。你想知道你可以从这些硬币的非空分组中得到多少不同的总和。
例如coins = [1, 2, 3] 和数量 = [1, 2, 2],有 11 个可能的和,基本上都是 1 到 11 之间的所有数字。
数组硬币的长度最多只能达到20,但数量[x]最多可以达到10^5。
什么是可能的高效算法解决方案。收集如此大量的所有可能组合将花费很长时间。有没有可以确定答案的数学公式?我不知道它会如何工作,尤其是它需要不同的总和。
我正在考虑根据硬币及其数量生成一个数组。基本上它的倍数:
[ [1],
[2, 4],
[3, 6]]
然后必须从每个数组中选择 1 或无。
1
1,2
1,4
1,3
...
1,4,6
不过,我似乎想不出一个好的算法来执行此操作。嵌套循环可能会太慢,因为可能有 20 个不同的硬币,每个硬币的数量可能很大。
另一种可能的解决方案是从 1 循环到最大值。其中最大值是所有硬币的总和乘以其相关数量。但问题在于确定是否存在等于该数字的子集。我知道有一个动态编程算法(子集总和)来确定是否存在一个子集,它会加起来达到某个值,但是数组是什么?
对于这个例子,它工作正常,列表为 [1,2,4,3,6],目标总和为 11,然后计算 DP 中的“真”将得到 11。但例如 coins = [10,50,100] 和 @ 987654326@。答案是 9 个可能的总和,但如果使用子集总和 DP 算法将得到 21“真”。如果提供的列表是 [10,50,100,100] 或 [10,50,100] 基于 [[10], [50, 100], [100]]
python 解决方案是首选,但不是必需的。
下面是我当前的代码,在 [10,50,100] 个硬币示例中得到 21。
def possibleSums(coins, quantity):
def subsetSum(arr,s):
dp = [False] * (s + 1)
dp[0] = True
for num in sorted(arr):
for i in range(1, len(dp)):
if num <= i:
dp[i] = dp[i] or dp[i - num]
return sum(dp)
maximum = sum((map(lambda t: t[0] * t[1], zip(coins, quantity))))
combinations = [[]]*len(coins)
for i,c in enumerate(coins):
combinations[i] = [ j for j in range(c,(c*quantity[i])+1,c) ]
array = []
for item in combinations:
array.extend(item)
print(subsetSum(array,maximum) - 1)
保证约束:
1 ≤ coins.length ≤ 20,
1 ≤ coins[i] ≤ 10^4.
quantity.length = coins.length,
1 ≤ quantity[i] ≤ 10^5.
保证 (quantity[0] + 1) * (quantity[1] + 1) * ... * (quantity[quantity.length - 1] + 1)
【问题讨论】:
-
这感觉可能是 NP 难的。我怀疑有一种方法可以将分区问题的某些变体减少为这个问题。
-
币值有限制吗?
-
我用限制/约束更新了问题。我忘了提到这是一个由大约 100 人解决了它的代码战造成的问题。所以肯定有一个解决方案可以在 4 秒(python)或 0.5(c++)内解决