【动态规划】零钱兑换问题-动态规划解题思路总结


今天大侠要讨论的题目是:【零钱兑换问题】
这是一道经典的动态规划问题. 此题目中,你将会看到动态规划全局最优解的体现。以及常见动态规划解题思路总结。

  1. 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1
来源:力扣

大侠借此题和大家说下动态规划 相比贪心算法的优势:

以上问题我们也可以用贪心算法(每次选择尽可能大的数值)来解释:

但是如果零钱集合是[1,5,7] 要配出的金额为10

则贪心算法会选择7和1,共需要4枚硬币
7 x 1 + 1 x 3 = 10 7x1+1x3 =10 7x1+1x3=10
但是最优解是选择5, 共需要2枚硬币
5 ∗ 2 = 10 5*2=10 52=10

由此可见,使用动态规划考虑全局问题是非常有必要的。

解决动态规划问题 主要分为以下三步:

  1. 目标(objection)
  2. 定义状态(define status)
  3. 转化方程(transform equation)

下面和大侠一起分析下这个问题吧!


目标 :凑成总金额所需的最少的硬币个数
定义状态:M(j)表示凑成j金额 所需的最少的硬币个数
转化方程:

M [ j + 1 ] = { m i n (   M [ j − c o i n s [ i ] ]   + 1   ) j > = c o i n s [ i ] a c c o u n t + 1 e l s e M[j+1] = \begin{cases} min( ~M[j-coins[i]]~+1~) &j>=coins[i] \\ account+1 & else \end{cases} M[j+1]={min( M[jcoins[i]] +1 )account+1j>=coins[i]else
以上公式的意思是,如果我当前要配出10 给出的硬币是1,5,7
由于 10 大于1,5,7 ,所以我有三种选择:
M ( 10 ) = m i n ( M [ 10 − 1 ] + 1 ,   M [ 10 − 5 ] + 1 ,   M [ 10 − 7 ] + 1 ) M(10)=min(M[10-1]+1,~M[10-5]+1,~M[10-7]+1) M(10)=minM[101]+1, M[105]+1, M[107]+1


下面讲下算法实现:
首先对于传入 参数的进行判断(boundary case)

  1. 如果account<0 不合法
  2. 如果coins为空 ,不合法
  3. 如果coins中的最小值都大于account ,无法配出

接下来循环计算记忆数组

  1. 初始化数组,并将m[0]=0 表示当account=0 时,不需要硬币
  2. 根据公式 循环计算每一个m的值

tips:
由于无法确定coins中的元素是否都满足条件 coins[i]<j
所有要通过for循环来逐个比较,并通过min_c 记录最少硬币数,等遍历结束coins ,将min_c 的数据写入m列表。account+1 作为无法配出的标志位。


# # 零钱兑换.py
# # description : 使用不同面值的硬币配出给定的面值.求最少硬币个数.
def solution(account,coins):
	# Definition: coins=[1,5,7] account = 10
	#boundary case
	if account <=0 :
		return 0
	if len(coins)==0:
		return -1
	if min(coins)>account:
		return -1
	#init memory sequence
	m=[-1 for _ in range(account+1)]
	m[0]=0
	for j in range(1,account+1):
		min_c=account+1#定义无法配出的标志
		for c in coins:
			if c<=j:
				min_c =m[j-c] if m[j-c]<min_c else min_c
		m[j]=min_c +1 if min_c <account+1 else account+1
	if m[-1]==account+1:#表示无法配出
		return -1
	else:
		return m[-1]#M[-1]表示配出account所需要的最少硬币数。
print(solution(10,[1,5,7]))

有什么问题,欢迎留言讨论。如果有幸帮到你,欢迎点赞哦,摸摸哒!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值