题解 | #兑换零钱(一)#
兑换零钱(一)
https://www.nowcoder.com/practice/3911a20b3f8743058214ceaa099eeb45
# 最少货币数
# @param arr int整型一维数组 the array
# @param aim int整型 the target
# @return int整型
#
class Solution:
def minMoney(self , arr: List[int], aim: int) -> int:
# 首次提交的答案 (19/24,超时)
# 提示1,dp:由题目给出的复杂度提示可看出,本题可转化为状态压缩的非0-1背包问题,其中item - n,weight - aim
# 算法复杂度:最差情况,不同类型货币价格均为1,O(aim^2*n)
if aim == 0:
return 0
dp = [0 for _ in range(aim+1)]
n = len(arr)
for i in range(n):
for j in range(aim, 0, -1): # 逆序遍历
for k in range(j // arr[i] + 1): # j - arr[i] * k >= 0, 向下取整
if dp[j - arr[i] * k] == 0 and j - arr[i] * k != 0:
continue
if dp[j] != 0:
dp[j] = min(dp[j], dp[j - arr[i] * k] + k)
else:
dp[j] = dp[j - arr[i] * k] + k
if dp[-1] == 0:
return -1
else:
return dp[-1]
# 标准答案,先迭代目标面值aim而非迭代货币种类n
# 1.因为目标面值aim迭代步长小于货币面值,每种货币的张数k的迭代可分散于aim的迭代中,复杂度:O(aim^2*n)
# 2.由于货币种类n非逆序迭代,因而面值aim的每轮迭代,不同种类货币可重复挑选(货币种类n迭代时,dp数组状态会覆盖)
# 算法复杂度:O(aim*n)
#小于1的都返回0
if aim < 1:
return 0
#dp[i]表示凑齐i元最少需要多少货币数
dp = [(aim + 1) for i in range(aim + 1)]
dp[0] = 0
#遍历1-aim元
for i in range(1, aim + 1):
#每种面值的货币都要枚举
for j in range(len(arr)):
#如果面值不超过要凑的钱才能用
if arr[j] <= i:
#维护最小值
dp[i] = min(dp[i], dp[i - arr[j]] + 1)
#如果最终答案大于aim代表无解
if dp[aim] > aim:
return -1
else:
return dp[aim]