题目为给定不同面值的n种硬币,面值加起来等于一个特定的数m,求最少需要多少枚硬币实现。
这个问题如果使用暴力求解,需要穷举所有可以加起来等m的组合,时间复杂度为O(m^n)。
def coin_27_plus_1(x):
coins = [2,5,7]
len_s = [x//coin for coin in coins]
sum = 0
times = 0
min_times = sys.maxsize
for i in range(len_s[0]+1):
for j in range(len_s[1]+1):
for k in range(len_s[2]+1):
sum = i*coins[0] + j*coins[1] + k*coins[2]
times = i + j + k
if sum == x and times < min_times:
min_times = times
return min_times
使用动态规划,可以将复杂度将为O(m*n)。分析该问题时,将n暂且设为[2,5,7],m设为27.
要使用动态规划,首先需要确定状态:
最后一步:最优策略中使用的最后一枚硬币,即有多少枚硬币,最后一步的最优策略就有多少种;
将其优化成子问题:使用最好的硬币拼出更小的面值27-;
其次确定转移方程:
然后确定初始条件和边界情况:
初始条件:
边界情况:如果n中的所有硬币组合都不能评出x(包括负数),则;
最后使用动态规划就是为了在计算下一项时可以直接已经计算的项,如,此时f[6]已经计算过,此时就不需要重复计算,代码如下:
def coinChange(self, coin_list, tg_num):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
# 最大值变量
import sys
max_v = sys.maxsize
# 创建一个数组,保存计算的值
funcs = [None]*(tg_num+1)
# 初始条件
funcs[0] = 0
# 依次从小到大计算
for s_v in range(1,tg_num+1):
funcs[s_v] = max_v
# 实现转移方程
for coin_v in coin_list:
# 设定边界条件
if s_v >= coin_v and funcs[s_v-coin_v] != max_v:
funcs[s_v] = min(funcs[s_v-coin_v]+1,funcs[s_v])
# 如果计算的值仍然是极大值,直接返回-1
if funcs[tg_num] == max_v:return -1
return funcs[tg_num]
动态规划在解决求解极值问题时是一种很有意思的算法,只要按步骤定义好状态、转移方程和边界,就可以一步步完成,谨以此记录。
解决该问题时,动态规划参考了九章算法。