动态规划:找凑零钱

问题:一个地区有三种钞票,币值分别为{1、5、11},要凑够15元,最少需要张钞票。
按照贪心算法的策略,先凑出最大面值的11,剩下的4个分别对应四个1元的钞票,这总共需要5张钞票。实际上,3张五元的钞票也能凑够15元。

可以用动态规划解决这个问题,要凑够n元钱,只要 n 不为0,考虑最后一次凑出的钞票,这有三种情况:

  • 最后一张是11,那么剩下n-11(如果n-11 > 0),这个时候问题又变成了凑出 n-11 最少需要多少张钞票。
  • 最后一张是 5,1,同理,问题变凑出 n-5,n-1 最少需要多少个张钞票(n - 1 >0)。币

这样就把原有的大问题转化为同类型的小问题。如果用 f(n) 表示凑出 n 最少需要的钞票张数,从前面的分析可知:如果计算出f(1), f(2), ..., f(k),就能计算出f(k+1)。从归纳计算的角度看,就解决了f(n)的计算问题。归纳计算的公式为

f(n) = \max (f(n-k) + 1: n-k\ge 0, k\in coins)

其中coins = {1, 5, 11}。

利用这个归纳计算公式,可以编写计算f(n)的程序。

只计算f(n)的程序

引入字典record,这个字典记录凑出 n 元钱的最小钞票张数,如果f(n) = q,则记录成 record[n] = q。

def func_for_ex(coins,amount): # coins是规定的钞票集,amount是要凑出的数额。
    record = {0:0} 
    N = 1
    while N <= amount: # 就是一个循环,如果填好了record[1] 到 record[N],然后填 record[N-1]
        filtered = [N - coin for coin in coins if N - coin >=0]
        temp = min([record[k] + 1 for k in filtered])
        record[N] = temp # 管理record 数表。
        N += 1
    return record

coins = [1,5,11]
N = 170
num = func_for_ex_3(coins,N)
print('凑出{}元需要最少{}张钞票'.format(N,num[N])) # 
print(num[N])

返回结果:

凑出170元需要最少16张钞票

给出具体凑法的算法

再引入一个字典how_to,这个字典记录用最少张数凑出 n 元钱钞票集合。比如:如果how_to[6] = [1,5],表示凑出6元最少张数的凑法是 1元,6元。

def func_for_ex_how(coins,amount):
    record = {0:0} # 记录已算出的最少凑法,格式是字典 record[8] = 3 表示凑出 8 的最少张数是 3. 
    how_to = {0:[]}
    N = 1
    while N <= amount: # 就是一个循环,如果填好了record[1] 到 record[N],然后填 record[N-1]
        min_value = float('inf')
        for coin in coins: 
            if N - coin < 0:
                continue
            if record[N-coin] + 1 < min_value:
                min_value = record[N - coin] + 1
                the_coin = coin
        record[N] = min_value # 管理record 数表。
        how_to[N] = how_to[N - the_coin].copy()
        how_to[N].append(the_coin)
        N += 1

    return record, how_to

coins = [1,5,11]
N = 170
num,how = func_for_ex_how(coins,N)
print('凑出{}元需要最少{}张钞票'.format(N,num[N])) # 
print('需要的用到的钞票是{}。'.format(how[N]))

 返回结果:

凑出170元需要最少16张钞票
需要的用到的钞票是[11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 5]。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值