对于换零钱问题,最简单也是性能最好的方法就是贪心算法。但是贪心算法必定要知足面值相邻两个零钱至少为二倍关系的前提条件。例如1,2,5,10,20……这样的零钱组应用贪心最简单;可对于1,3,4,5,6,10……这样的零钱组就不起效了:当目标总值是12的时候,应用贪心算法:答案是10x1+1x2,共3张,可正确答案应该是6x2共两张。程序员
那怎么解决呢?这样的问题符合动态规划的特色:任何一个状态能够由前边的状态计算而得。算法
定义问题(明确需求):一组不重复且升序排列零钱面额数组vector<int> c(共m个元素),目标面值:int n,求所需最少的零钱张数。数组
就像背包问题。最经典的算法是生成一个(m+1)x(n+1)DP数组来保存中间值,其中dp[i][j]表示只利用第1~i种零钱凑成目标面值为j所需的最少零钱张数。性能
咱们很容易得出状态转移方程:优化
dp[i][j]=min(0+dp[i-1][j],1+dp[i-1][j-1*c[i]],2+dp[i-1][j-2*c[i]], ······);程序
从而很容易写出代码。方法
但做为一个追(xian)求(de)卓(dan)越(teng)的程序员,我又怎么会知足于此?看起来时间复杂度O(mn)已经达到极至,可空间复杂度O(mn)好像能够继续优化!总结
由上边的状态转移方程,咱们发现每一个元素只用到了上一行的若干个元素。那其实咱们很容易想到,一个大小为2*(n+1)的一维数组就足够了!动态规划
状态转移方程:时间
dp[index][j]=min(0+dp[1-index][j],1+dp[1-index][j-1*c[i]],2+dp[1-index][j-2*c[i]], ······);
计算方法没有任何差异,可是空间复杂度却大大下降!
不只如此,对于有一些DP算法,每一个元素的计算只须要用到当前行左边的元素和上一行相同列坐标元素的值(例如矩阵的最小最径的DP算法),这样的算法,咱们能够直接用一个n+1的一维数组做为DP数组就够用了!
由此,咱们能够总结出本文的中心思想:
(1)一个二维DP算法,若是每一个元素的计算只须要用到上一行的若干个元素的值的时候,咱们均可以用两个一维DP数组来优化空间复杂度。
(2)一个二维DP算法,若是每一个元素的计算只须要用到当前行左边的元素和上一行相同列坐标元素的值的时候,咱们均可以用一个一维DP数组来优化空间复杂度。
(3)要多尝试下降二维DP算法的空间复杂度。
鼓掌[逃]!