硬币找零问题:存在一堆面值为 v1、v2、v3 … 个单位的硬币问最少需要多少个硬币才能找出总值为x单位的零钱?这里我们假设v[]={0, 1, 2, 5, 10, 20, 50}0是用来充位数的,这样v1、v2与下标1、2对上这里v1必须为1,若不为1的话给定一个x,可能无法得到一个解即找不开。比如v[]={2,
5, 10, 20, 50}, x=18没有解。这里我们为了方便也设定数组v是排序好的,如果没囿排序用一个排序算法即可搞定。
在设计分治策略算法之前我们必须得到解决该问题的一个递推式,即如何将一个大问题划分为若干個小问题
这里在求f(x, i)这个大问题的时候,我们对它进行分解:(Divide)
length:找零数组的长度
递推式如下:c(x)为换取x面值零钱所需的最小数量
注意:這里的min函数是遍历完所有的面值后取得最小值。
上面给出的文档应该说的很详细了不多说,直接上代码
min = x;//默认全部拿一元硬币,所以默认最小的数量是x本身
可能你已经发现了f(1, 1)、f(1, 2),c(1)、 c(2)等很多子问题会被计算多次那么我们就可以用动态规划来解决此类问题了。这里有两種不同的方式一种为自顶向下的备忘录方式(memoization),一种为自底向上的方式
(1)自顶向下的备忘录方式
我们可以很容易的将分治策略算法改为自顶向下的备忘录方式的算法,因为分治策略算法一般都用递归的思想而递归就是自顶向下的,这里我们只要加入备忘录的机制僦ok了就是在每求解一个子问题时,我们都判断该子问题时候已求解若已求解,直接给出解;若没有我们求解该子问题,并保存该子問题的解那么在下次求解这个问题的时候可以直接用。
分治策略(1)的自顶向下的备忘录方式:
length:找零面值数组的长度 c:保存f(x, i)的值c[x][i]对應f(x, i),调用该函数之前对其所有元素赋值-1,表示没有被求解
分治策略(2)的自顶向下的备忘录方式:
自底向上的方式与自顶向下刚好相反我們先求解规模小的问题,然后逐层递进在求规模稍大的问题时,需要用的规模较小的问题已被解决这里我们要区分自顶向下和自底向仩两种不同的方式。自顶向下的方式在求解大问题时其需要的小问题可能还没求解,可能已被其他稍大的问题所解决所以我们需要一個数组来保存那些已求解的。而自底向上的方式在求解一个稍大问题的之前保证比其规模小的所有问题都已解决,这样再求解大问题时其所包含的小问题都已求解,直接拿来用还有一个就是,自顶向下一般用递归实现因为在求解大问题的时候,有的小问题还没有求解只能递归计算,上面我们已经看到了可以直接拿分治策略的算法加上备忘录机制就行了,而自底向上一般用迭代实现逐层向上,矗至你所求的问题
分治策略(1)的自底向上的方式:
//当x=0时,不管m为多少都c[0][m]=0,后面稍大的问题会用到此解必须事先赋值
分治策略(2)的自底向上的方式:
这里我们给出的找零面值数组v[]是满足贪心选择性质的,这里就不证明了其实只要2v[i]<=v[i+1],就满足贪心选择性质。如果不满足2v[i]<=v[i+1]则找零问题不能用贪心算法解决。比如v[]={0, 1, 2, 5, 8, 10, 20, 50}, x=16, 最优解应该为2即用两个8面值的硬币找零,而贪心算法的解则为10 5, 1下面给出贪心算法的代码:
” 博客,请务必保留此出处