前言:实习期间都在做业务,降低了对算法题编程的练习。回来秋招答笔试题,遇到动归的题做得细碎,所以趁着还有一点时间把经典的动归问题用最通俗的话总结一下,顺便自己也捡捡编程能力。
动态规划的基本概念实际中就是如下几个步骤:
a.分析最优解的性质,并刻画结构特征。
b.递归定义的最优解
c.以自底向上或自顶向下的方式计算出最优解
d.根据计算优值得到的信息,构造问题的最优解。
问题描述:
有数组penny,penny中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim(小于等于1000)代表要找的钱数,求换钱有多少种方法。
测试样例:
n=3,aim=,penny【2,1,4】
返回值为:
9
解析:
首先,我们从题目中可以得知我们有3种不同面值的货币,和要达到找钱的目标(aim)。
其次,我们可以将找钱的任务划分成如下的小任务。以3种货币种类举例。
大致的想法就是我就用一种货币去凑0~aim的面值。看看当使用一种货币的时候,从0~aim这些面值中每种面值的零钱换钱方法有多少种。之后,我在第一种货币的基础上增加第而种货币,这时候问题就变为使用使用两种货币的时候,从0~aim这些面值中每种面值的的零钱换钱方法有多少种。在这个时候我们会发现,对于一个面值而言,它的换钱的方法是包括两方面。一方面是使用一种货币的进行兑换的方法数量。另一种就是使用两种货币兑换的方法数量。以此类推,当我们加入第三个货币的时候,此时兑换的方法是由使用两种货币兑换的方法数量和使用三种货币兑换的方法数量组成的。这样我们就会将一个面值的兑换问题大体上建立一个概念。虽然可能还是有点绕,接下来就是具体分析如何实现了。
我们先建立一个数组dp[n][m],n就是货币的种类数量,m就是aim+1。dp[n][m]用来表示使用前n种货币兑换m面值的种数。根据我们前面的分析。会出现以下两种状况。
第一种就是使用第n种货币:dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]]。
这里面dp[n-1][m]为兑换m面值使用n-1种货币时的兑换方法数量。dp[n][m-peney[n]]为兑换m-peney[n]面值使用n种货币时的兑换方法。
第二种就是无法使用第n种货币(货币的面额比需要兑换的m值要大):dp[n][m] = dp[n-1][m] 这个时候使用n种货币去兑换面值零钱的方法种数和适用n-1种货币去兑换m面值零钱的方法总数是一样的。
实例讲解
下面我们就以面值为【2,1,4】 aim=9的情况通过excel具体表现一下填表的过程。
第一步.通俗点就是把二维数组的边界值都填好。第一行很好理解就是,我只用面值2的货币去凑9。结果就是凑面值0只有一种方法就是一张不拿。凑2就是拿一张,也是一种方法。凑4就是拿两张,依旧只有一种方法。类推凑8就只有拿4张面值2的这一种方法。第一列也好理解,凑0之后啥也不拿这一种方法。
第二步.这时候我们就要填第二行了,也就是使用两种货币去凑钱这行。那绿框里的一是怎么来的呢。首先我们可以知道待凑的面值1大于等于货币的面值。因此这时候我们是可以使用第三种货币的。根据上面的等式dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]],我们可以计算出绿框中的数值为1。也就是下图中两个红框内数值的和。
第三步以此类推,使用两种货币这一行也被我们填满了。
第四步.就是填第三行了。这是货币的面值是4,已经比一些要凑的面值都要大了。此时我们会遇到无法使用第四种面值的情况。这时dp[n][m] = dp[n-1][m]。因此此时紫色框内的数值与上一行是一样的。
第五步,就是当待凑面值大于等于货币面值的时候。此时的等式关系又变为dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]]。此时整个数组的值就填完了。右下角的值就是使用3种货币去凑9块钱的时候有多少种方法。
最后是程序实现: