找零兑换问题——一个案例搞懂贪心、递归、动态规划

  • Post author:
  • Post category:其他


找零时,兑换最少个数的硬币



一、贪心策略

贪心,就是每次都试图解决问题的

尽量大

的一部分

从最大面值的硬币开始,用尽量多的数量,有余额的再到下一个面值

# 贪心
def change(coinValueList, money):
    '''
    贪心策略
    :param coinValueList: 硬币体系
    :param money: 找零的币值
    :return: 找零所需硬币数
    '''
    coinValueList = sorted(coinValueList, reverse=True)
    num = [0] * len(coinValueList)
    for k in range(len(coinValueList)):
        num[k] = money // coinValueList[k]
        money = money % coinValueList[k]
    if money == 0:
        return sum(num)

print(change([1, 5, 10, 25], 63))
print(change([25, 21, 10, 5, 1], 63))
print(change([50,25,10,5,1], 49))
def change(coinValueList, money):
    '''
    贪心策略简化
    :param coinValueList: 硬币体系
    :param money: 找零的币值
    :return: num 找零所需硬币数
    '''
    coinValueList = sorted(coinValueList, reverse=True)
    num = 0
    for c in coinValueList:
        if c <= money:
            num += money // c
            money = money % c
    if money == 0:
        return num

print(change([1, 5, 10, 25], 63))
print(change([25, 21, 10, 5, 1], 63))
print(change([50,25,10,5,1], 49))

其实币值是按贪心策略设计的,对于某些情况贪心策略会失效。如果币值特殊,比如有21元,币值体系有25,21,10,5,1,那么对于找零63元,最少硬币数是3,但是如果按贪心策略,最少硬币数是2+1+3 = 6



二、递归解法

递归体现了分治策略split and merge,递归三定律:基本结束条件、缩小规模、调用自身。

在这里插入图片描述

在这里插入图片描述

def change(coinValueList, money):
	if money in coinValueList:  # 结束条件
		return 1
		
	min = money
	for i in [c for c in coinValueList if c <= change]:  # 缩小规模
		num = 1 + change(coinValueList, money - i)  # 每次减去一种币值
		if num < min:  # 每次挑选最小数量
			num = min  # for循环里面这个结构常用于找到每次最小或最大
		return min 

print(change([1, 5, 10, 25], 63))
print(change([25, 21, 10, 5, 1], 63))

在这里插入图片描述

import time
# 不知为啥,我用上面的clock不行
t1 = time.process_time()
print(change([1, 5, 10, 25], 63))
t2 = time.process_time()
print('time:', t2-t1)

在这里插入图片描述

在这里插入图片描述

def change(coinValueList, money, knownResults):
	min = money
	if money in coinValueList:
		knownResults[money] = 1
		return 1
	elif knownResults[money] > 0:  # 查表成功
		return knownResults[money]
	else:
		for i in [c for c in coinValueList if c <= money]:
			num = 1 + change(coinValueList, money - i, knownResults)
			if num < min:
				min = num
				knownResults[money] = min
	return min


print(change([25, 21, 10, 5, 1], 63, [0] * 64))			

在这里插入图片描述

缓存:还有很多其它应用



三、动态规划解法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

def dpchange(coinValueList, money, min):
    # 从1分开始到money逐个计算最少硬币数
    for cents in range(1, money + 1):
        # 1. 初始化一个最大值
        coinCount = money
        # 2. 减去每个硬币,向后查最少硬币数,同时记录总的最少数
        for j in [c for c in coinValueList if c <= cents]:
            if min[cents - j] < coinCount:
                coinCount = min[cents - j] + 1
        # 3. 得到当前最少硬币数,记录到表中
        min[cents] = coinCount
    # 返回最后一个结果
    return min[money]

print(dpchange([1, 5, 10, 21, 25], 63, [0] * 64))
print(dpchange([1, 5, 10, 25], 63, [0] * 64))

在这里插入图片描述

在这里插入图片描述

def dpchange(coinValueList, money, min, coinsUsed):
    # 从1分开始到money逐个计算最少硬币数
    for cents in range(money + 1):
        # 1. 初始化一个最大值
        coinCount = money
        newCoin = 1   # 初始化一下新加硬币
        # 2. 减去每个硬币,向后查最少硬币数,同时记录总的最少数
        for j in [c for c in coinValueList if c <= cents]:
            if min[cents - j] < coinCount:
                coinCount = min[cents - j] + 1
                newCoin = j  # 对应最小数量,所减的硬币
        # 3. 得到当前最少硬币数,记录到表中
        min[cents] = coinCount
        coinsUsed[cents] = newCoin  # 记录本步骤加的一枚硬币
    # 返回最后一个结果
    return min[money]

def printCoins(coinsUsed, money):
    coin = money
    while coin > 0:
        thisCoin = coinsUsed[coin]
        print(thisCoin)
        coin = coin - thisCoin


money = 63
clist = [1, 5, 10, 21, 25]
coinsUsed = [0] * (money + 1)
coinCount = [0] * (money + 1)

print('Making change for', money, "requires")
print(dpchange(clist, money, coinCount, coinsUsed), 'coins')
print('They are:')
printCoins(coinsUsed, money)
print('The used list is as follows:')
print(coinsUsed)

Making change for 63 requires

3 coins

They are:

21

21

21

The used list is as follows:

[0, 1, 1, 1, 1, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 21, 21, 21, 21, 25, 25, 25, 25, 25, 25, 21, 21, 21, 21, 25, 25, 25, 25, 25, 25, 21, 21, 21, 21, 25, 25, 25, 25, 25, 25, 25, 21, 21, 21, 25, 25, 25, 25, 25, 25, 25, 21, 21]

好像有点问题哎,求大佬指点



版权声明:本文为m0_67245659原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。