问题:一个地区有三种钞票,币值分别为{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)的计算问题。归纳计算的公式为
其中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]。