我找到了解决此问题的方案,但需要O(n ^ 2)。有可能做得更好吗?
问题:假设我们想要为D美元做出改变。我们有一个包含N个元素的数组A.面额作为美元值存在于数组中,但我们不知道先进的确切面额。但是,我们给出0 <&lt; A [j]&lt; 125 * N。限制是,我们只有6种每种类型的面额,我们必须能够确定我们是否可以使用完全 6总票据进行更改(我们可以重复账单并假设账单有任何类型,所以我们可以有4美元的钞票。)
例: 如果A = [3,4,6,5,20,18,10,30]且D = 50.那么算法从5 + 5 + 5 + 5 + 10 + 20返回真。
我的尝试:
我尝试排序然后分割然后我卡住了因为我不知道如何消除可能的选择,因为我不知道数组中究竟是什么。更好的是,如果没有明确地在O(n ^ 2)时间内完成,我不确定如何确定它是不可能的。是否有可能利用这样一个事实:我知道我只限于6张账单?
答案 0 :(得分:0)
对我而言,这看起来像是典型的递归问题。让我们编写一个函数来检查我们是否可以对D
美元进行更改。为此,我们将采取第一个账单(假设它是3美元),将其从D
中删除,然后递归检查我们是否可以对D - 3
美元进行更改。
如果我们不检查已经检查过的组合,我们可以更快地使这个解决方案。因此,如果我们已经知道账单3, 5, 10
不符合我们的需求,那么我们也不需要检查组合5, 10, 3
。为此,我们首先需要对A
数组进行排序,然后将最后使用的帐单(last_bill_id
)的数量传递给check
函数。在函数内部,我们不需要检查数字小于last_bill_id
的账单的任何组合。
python中的完整解决方案:
A = [3, 4, 6, 5, 20, 18, 10, 30]
D = 50
def check(counters, current_sum, depth, last_bill_id):
global A
if depth > 6: # max amount of bills is 6
return False
if depth == 6: # we used 6 bill, did we get the correct sum?
return current_sum == 0
if current_sum <= 0: # we gave too much change
return False
# current_sum > 0 and depth < 6
for i in xrange(last_bill_id, len(A)):
if counters[i] < 6:
# we can use i-th bill another time
counters[i] += 1
if check(counters, current_sum - A[i], depth + 1, i):
return True
counters[i] -= 1
return False
# init counters with zeros
counters = [0] * len(A)
# check if we can change for `D`
A = sorted(A) # sort A before the function
print 'Can make change:', check(counters, D, 0, 0)
# print bills with counters
for i, c in enumerate(counters):
if c > 0:
print '$%d x %d' % (A[i], c)
输出:
可以进行更改:True
$ 3 x 4
$ 18 x 1
$ 20 x 1
以前的解决方案具有复杂性O(n^6)
。但实际上我们可以使用memoization(或者,我们用另一种方式,dynamic programming)使速度更快。让我们对A
数组进行排序并重复其中的每个数字6次,这样我们就会得到像A = [3, 3, 3, 3, 3, 3, 5, 5, ...]
这样的数字。现在让我们填充3D矩阵M[,,]
,其中M[bills_num, i, d]
是true
,如果我们可以使用d
帐单更改bills_num
帐单,从{{1}开始} i
数组的位置。结果将在单元格A
中。此矩阵的大小为M[6, 0, D]
,因此我们可以在6 x (6 * n) x D
时填充它(使用类似于之前的解决方案的递归方法)。 python中的代码:
O(6 * (6 * n) * D) == O(n * D)