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

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

一、贪心策略

贪心,就是每次都试图解决问题的尽量大的一部分
从最大面值的硬币开始,用尽量多的数量,有余额的再到下一个面值

# 贪心
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]
好像有点问题哎,求大佬指点

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值