问题:如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
动态规划的本质是将原问题分解为同性质的若干相同子结构,在求解最优值的过程中将子结构的最优值记录到一个表中以避免有时会有大量的重复计算。
例如硬币组合问题,若求凑够11元的最少硬币数,可以先从凑够0元、1元、2元……的子结构开始分析。
假设d(i)为凑够i元所需最少硬币数,则
d(0) = 0 理所当然
d(1) = 1 要凑够1元,需要从面值小于等于1元的硬币中选择,目前只有面值为1元的硬币
此时d(1) = d(0) + 1
d(2) = d(2 - 1) + 1 = 2, 从面值小于等于2元的硬币中选择,符合要求的硬币面值为:1元。
此时d(2) = d(2-1) + 1
d(3) = d(3 - 3) + 1 = 1, 从面值小于等于3元的硬币中选择,符合要求的硬币面值为:1元,3元。
此时有有两种选择:是否选择含有面值3元的硬币
含有3元硬币:d(3) = d(3 - 3) + 1 = 1
不含3元硬币:d(3) = d(3 - 1) + 1 = d(2) + 1 = 3
自然是选择二者中较小值
依次类推...
就该问题总结一下,随着要凑够钱数的增加:
1.首先要知道所有不大于该钱数的面值,
2.对于每种面值的硬币,求出当选择一个该面值的硬币时所需的硬币数
当选择一个硬币后,所需硬币数+1,所要凑够的钱数=原所要凑的钱数-该硬币面值,所要凑够的钱数减少,求减少后要凑钱数最少所需硬币数,属于原问题的子结构,已求出解
3.在上述求出的结果集中,选择最小值,即为要凑够该钱数所需的最少硬币数
由此可以看出,每个问题的最优值都是借其子结构的最优值得到的。
而该算法的最小的子结构的最优解是已知的,即:当要凑钱数为0元时,最少需要0枚硬币。
利用这个最小的子结构,通过递推式便可求出所指定值凑够钱数的最优值
上面所提到的递推式,便是状态转移方程。利用已知状态,不断通过状态转移方程求解,便得到了最优值和最优解。
下面看一下硬币组合问题的数学描述:
d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j个硬币的面值,i表示要凑够i元,d(i)表示凑够i元最少需要的硬币数。即:
0 i == 0 时 min_coin_num(i) = { min{ min_coin_num( i-coin_value(j) )+1 | i-coin_value(j)>0} coin_value(j)表示第j种硬币的面值 i > 0 时
当总值total_value为i时, 对于所有的 coin_value(j) < i的硬币j ,取min{ min_coin_num(i-coin_value(j)) }
最后,该算法的python实现:
# 如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元
2
3
4
5 def select_coin(coin_value, total_value):
6 min_coin_num = [0]
7 for i in range(1, total_value + 1):
8 min_coin_num.append(float('inf'))
9 for value in coin_value:
10 if value <= i and min_coin_num[i - value] + 1 < min_coin_num[i]:
11 min_coin_num[i] = min_coin_num[i - value] + 1
12
13 return min_coin_num
14
15
16 result = select_coin([1, 3, 5], 11)
17 print("coin number:" + str(result[-1]))