兑换零钱(一)

描述 给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。 如果无解,请返回-1.

数据范围:数组大小满足 0≤n≤10000 , 数组中每个数字都满足 0<val≤10000,0≤aim≤5000

要求:时间复杂度 O(n×aim) ,空间复杂度 O(aim)。 思路:这道题我还真不太会,考察动态规划,我一开始想的是将arr数组中小于aim的取出来,然后排序得到arr_sort。之后将aim除以arr_sort中最大的,得到的余数是新的aim,最后aim等于0的时候,计算张数,如果除以arr_sort[0]还不能为0,则取出最后放进去的一张arr_sort[cur],然后aim = aim + arr_sort[cur],再count = count - 1,count += (int)(aim/arr_sort[cur-1])。aim = aim%arr_sort[cur-1]。后来想了一下,不对,若aim = 10,arr = [5,8,1],就会出问题。

之后我想到的是类似于跳台阶,最后一张为arr[i],那么张数就为arr[i]+1,比如arr = [2,3,5],aim=999,那么结果就应该是f(999) = min(f(999-2),f(999-3),f(999-5))+1,当时我想到了递归,但是如果递归复杂度肯定高,所以要和跳台阶类似,从f(0)开始计算,用for循环从小到大计算f(n),直到f(999),而f(0) = 0。然后这里我就开始纠结了,因为我们要找最小值,而f(n)显然有部分值是-1,不能兑换的。我总感觉答案快出来了,但是就是卡住了,之后我看了一下评论,发现最终很好解决,初始化f(n)为aim+1即可。用一个数组aim_list将f(n)的结果存起来,然后更新aim_list的值,f(n) = min(f(n - arr[i])) + 1

# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 最少货币数
# @param arr int整型一维数组 the array
# @param aim int整型 the target
# @return int整型
#
class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        # write code here
        aim_list = [aim+1]*(aim+1)
        aim_list[0] = 0
        for i in range(1,aim+1,1):
            for j in arr:
                if j <= i:
                    aim_list[i] = min(aim_list[i],aim_list[i-j]+1)
        if aim_list[aim] == (aim + 1):
            return -1
        else:
            return aim_list[aim]