找出哪些硬币占总数的最小值

时间:2016-01-30 01:09:02

标签: python

给定一个硬币阵列[1, 3, 7, 12]和一个总数(29)找到需要弥补金额的最小硬币数量(正确答案为4)。

我的下面的代码正确地找到了最小数量的硬币,但没有使用哪些硬币来获得最低金额。我尝试使用字典来做到这一点,但我的数字非常高。

def changeSlow(coinValueList, total, coinDict):
    if total == 0:
        return 0

    res = sys.maxint

    for i in range(len(coinValueList)):
        if coinValueList[i] <= total:
            sub_res = changeSlow(coinValueList, total-coinValueList[i], coinDict)

            if sub_res != sys.maxint and sub_res + 1 < res:
                res = sub_res + 1

        if coinValueList[i] not in coinDict:
            coinDict[coinValueList[i]] = 0
        else:
            coinDict[coinValueList[i]] += 1

    return res

coins = [1, 3, 7, 12]
coinDict = {}
print(changeSlow(coins, 29, coinDict)) >> gives 4 correctly
print(coinDict) >> gives {1: 190992, 3: 190992: 7: 190992, 12:190992}

我得到的输出不正确,不知道为什么我错了:

>> 4
>> {1: 190992, 3: 190992: 7: 190992, 12:190992}

有人可以给我一些关于我做错的提示吗?我应该提一下,我必须使用某种递归来实现这个解决方案。

3 个答案:

答案 0 :(得分:1)

这确实应该用DP完成,但是你的功能名称表明你已经认识到了这一点。对于[1,7,11]和14美分的情况,贪婪的解决方案失败了,所以你不能一般地使用它。你只能在特殊情况下使用贪婪的解决方案,而普通的[1,5,10,25,50,100]就是这样一个贪婪的案例。

下面是一个非常低效的递归解决方案。

def changeSlow(coinValueList, total):
  options=[]
  for coin in coinValueList:
    if coin < total:
      res=changeSlow(coinValueList, total-coin)
      if res:
        options.append([coin] + res)
    elif coin==total:
      return [coin]
  if options:
    return sorted(options, key=lambda x: len(x) )[0]
  return []

coins = [1, 3, 7, 12]
result=changeSlow(coins,29)
print result, len(result)

答案 1 :(得分:1)

5实际上并不正确。正确的答案是4(12,7,7,3)。你获得巨大数字的原因是因为你在整个函数中使用了相同的字典引用,因此每个递归调用都会增加相同的字典。您希望每次递归调用都使用并递增自己的字典。

正如Untitled123所提到的那样贪婪并不一定正确,因为它错过了选择最高硬币并不一定是正确答案的情况(这就是这个情况,因为选择12次而不是选择12次,然后7次两次得到5的答案,而不是4)。

这是我非贪婪的解决方案:

# Not really faster, but it avoids your huge numbers issue
def changeFast(coinValueList, total, numCoins, coinDict):

    # Base Case: we've reached the money total we want
    if total == 0:
        return (numCoins, coinDict)

    bestCoins = -1
    bestDict = {}

    for i in range(len(coinValueList)):

        # You need to pass a copy of the dictionary to the
        # recursive calls. Otherwise each recursive call will
        # update the same dictionary!
        dictCopy = {}
        for coin in coinValueList:
            dictCopy[coin] = coinDict[coin]

        coin = coinValueList[i]

        if coin <= total:

            # Increment the corresponding coin slot in the dictionary
            dictCopy[coin] += 1
            (subCoins, subDict) = changeFast(coinValueList, total - coin, numCoins + 1, dictCopy)

            # Update our best results so far
            if bestCoins == -1 or subCoins < bestCoins:
                bestCoins = subCoins
                bestDict = subDict

    return (bestCoins, bestDict)


coins = [1, 3, 7, 12]

baseDict = {}
for coin in coins:
    baseDict[coin] = 0

print(changeFast(coins, 29, 0, baseDict))
# Returns (4, {1: 0, 3: 1, 7: 2, 12: 1}) as expected

答案 2 :(得分:0)

这是我的递归解决方案。这是一个很好的方法,但我相信还有更好的方法。

def changeSlow(coinValueList, total, coinsDict):
    if total == 0:
        return 0
    if total >= max(coinValueList):
        total -= max(coinValueList)
        coinsDict[str(max(coinValueList))] += 1
    else:
        coinValueList.pop()
        return changeSlow(coinValueList, total, coinsDict)
    print(coinsDict)
    return 1 + changeSlow(coinValueList, total, coinsDict)



coins = [1, 3, 7, 12]
coinsDict = {'1': 0, '3': 0, '7': 0, '12': 0}
print(changeSlow(coins, 29, coinsDict))

这将是5个硬币的输出:

{'3': 0, '7': 0, '1': 0, '12': 1}
{'3': 0, '7': 0, '1': 0, '12': 2}
{'3': 1, '7': 0, '1': 0, '12': 2}
{'3': 1, '7': 0, '1': 1, '12': 2}
{'3': 1, '7': 0, '1': 2, '12': 2}
5

如果您需要任何澄清,请与我们联系。希望这会有所帮助。