赞
踩
在我们使用递归算法时,可能会出现规模庞大的重复计算,用一个中间表记录每个计算过的最优解法,就可以避免大量的重复计算。中间结果记录可以很好解决找零兑换问题。实际上,这种方法还不能称为动态规划,而是叫做“memoization(记忆化/函数值缓存)”的技术提高了递归解法的性能。
动态规划算法采用了一种更有条理的方式来得到问题的解。
找零兑换的动态规划算法从最简单的“1分钱找零”的最优解开始,逐步递加上去,直到我们需要的找零钱数。
在找零递加的过程中,设法保持每一分钱的递加都是最优解,一直加到求解找零钱数,自然得到最优解。
递加的过程能保持最优解的关键是,其依赖于更少钱数最优解的简单计算,而更少钱数的最优解已经得到了。
问题的最优解包含了更小规模子问题的最优解,这是一个最优化问题能够用动态规划策略解决的必要条件。
originalamount找零兑换问题具体来说就是:
numCoins 就是当前需要找零的硬币个数。
1 5 10 25就是硬币的币值。
originalamount就是要找零的钱数。
例如用动态规划解决11分钱的兑换问题。
兑换5分钱的时候,最优解就是一个5分的硬币。
25分就不减了,因为明显 11 < 24,不可能用25分来找零。
解决思路如下:
need_change = 65 init_min_coins = [0] * (need_change + 1) # 建一个空表 real_value_list = [1, 5, 10, 25] def dynamic_programming(change, min_coins, value_list): for current_change in range(1, change + 1): min_coins[current_change] = current_change # 最坏的情况是只用一分钱兑换 for value in value_list: # 遍历每个币值 if current_change >= value: if 1 + min_coins[current_change - value] < min_coins[current_change]: # 若使用当前币值时,需要的最小硬币个数最小,就更新最小硬币数表中的个数 min_coins[current_change] = 1 + min_coins[current_change - value] return min_coins[change] # 直接查表,最后一个就是我们想要的结果 print(dynamic_programming(need_change, init_min_coins, real_value_list))
我们注意到动态规划算法的dynamic_programming并不是递归函数。虽然这个问题是从递归算法开始解决,但最终我们得到一个更有条理的高效非递归算法。
动态规划中最主要的思想是:
前面的算法已经得到了最少硬币的数量,但没有返回硬币如何组合。
扩展算法的思路很简单,只需要在生成最
优解列表同时跟踪记录所选择的那个硬币币值即可。
在得到最后的解后,减去选择的硬币币值,回溯到表格之前的部分找零,就能逐步得到每一步所选择的硬币币值。
need_change = 63 init_min_coins = [0] * (need_change + 1) # 建一个空表 init_coins_used = [0] * (need_change + 1) # 建一个空表用于保存每一步新增的硬币币值 real_value_list = [1, 5, 10, 21, 25] def dynamic_programming(change, min_coins, value_list, coins_used): for current_change in range(1, change + 1): min_coins[current_change] = current_change # 最坏的情况是只用一分钱兑换 for value in value_list: # 遍历每个币值 if current_change >= value: if 1 + min_coins[current_change - value] <= min_coins[current_change]: # 若使用当前币值时,需要的最小硬币个数最小,就更新最小硬币数表中的个数 min_coins[current_change] = 1 + min_coins[current_change - value] coins_used[current_change] = value # 当前使用的币值存入到表中 return min_coins[change], coins_used # 直接查表,最后一个就是我们想要的结果 def combination_values(change, coins_used): print(coins_used[change]) while change > 1: # 每次显示并减去当前新增的硬币,就是回溯了使用的所有币值,即显示了每次新增的硬币的币值 if coins_used[change - coins_used[change]] > 0: print(coins_used[change - coins_used[change]]) change = change - coins_used[change] min_coins, coins_used = dynamic_programming(need_change, init_min_coins, real_value_list, init_coins_used) print(min_coins) print(coins_used) combination_values(need_change, coins_used)
3
[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]
21
21
21
本文的知识来源于B站视频 【慕课+课堂实录】数据结构与算法Python版-北京大学-陈斌-字幕校对-【完结!】,是对陈斌老师课程的复习总结
代码是我自己写的,功能比较简单
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。