给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,代表要找的钱数,求组成aim的最少货币数。
arr = [5, 2, 3],aim = 20,arr中组成20的最少张数为4,返回4。
arr = [5, 2, 3],aim = 0,不用任何货币就可以组成0,返回0。
arr = [3, 5],aim = 2,钱找不开返回-1 。
假设arr = [5, 2, 3],aim=10。
建立一个N*(M+1)的矩阵dp,N为arr里的个数,M=aim,dp[i][j]表示由当前行及该行上面所有行的货币种类兑换该列值的最少货币数,所在的列即需要兑换的钱数,dp初始化如下:
行\列012345678910500000000000200000000000300000000000首先,计算第一行可兑换的钱数,即用5能兑换的钱数,其它不可兑换的钱数用max表示,如下:
行\列01234567891050maxmaxmaxmax1maxmaxmaxmax22 3计算其它位置。对于dp[i][j],当前i行兑换j钱数时,m为i行对应的货币值,若dp[i][j]可以兑换开,则dp[i][j-m]位置也可以兑换,此时dp[i][j] = dp[i][j-m] + 1。同时需要对比上一行兑换该钱数使用的货币数,即dp[i-1][j]的值,取两者的最小值,即为最少货币数。用这个方法处理第二行,前两行显示如下:
行\列01234567891050maxmaxmaxmax1maxmaxmaxmax220max1max21324323用同样的方法处理第三行,如下:
行\列01234567891050maxmaxmaxmax1maxmaxmaxmax220max1max213243230max112122232使用python实现上面的过程,如下:
import sys def min_coins(arr, aim): if arr==None or len(arr)==0 or aim<0: return -1 n = len(arr) max = sys.maxsize dp = [[0]*(aim+1) for i in range(n)] for j in range(1, aim+1): dp[0][j] = max if (j-arr[0])>=0 and dp[0][j-arr[0]]!=max: dp[0][j] = dp[0][j-arr[0]] + 1 for i in range(1,n): for j in range(1, aim+1): temp = max if (j-arr[i])>=0 and dp[i][j-arr[i]]!=max: temp = dp[i][j-arr[i]] + 1 dp[i][j] = min(temp, dp[i-1][j]) return dp[n-1][aim] if dp[n-1][aim]!=max else -1输入arr和aim,调用上面的方法查看结果:
arr = [5, 2, 3] aim = 10 res = min_coins(arr, aim) print('换钱的最少货币数为:', res)结果如下:
换钱的最少货币数为: 2
若要查看dp矩阵的元素,可在输出前打印,或者同最少货币数一起返回。
参考:《程序员代码面试指南》左程云