有一批价格分别为p1,p2, p3 … pn的n种商品, 你手中持有金钱为money,如何购买商品使剩余的钱最少,求最少剩多少?
示例一:
输入 p=[150, 200, 350], money = 250
输出:50
示例二:
输入 p=[150, 200, 350], money = 500
输出:0
解法一,深度优先搜索。
# 深度优先搜索算法, 个人感觉这种算法就是将多层for循环扁平化
def leastMoneyLeft(money, prices):
goods = [money // x for x in prices] # 钱数能买的各种商品最多的数量
dfs(prices, 0, money, goods)
def dfs(prices, step, moneyLeft, goodsCount):
'''
prices 价格列表
step 第几次选择
monkeyLeft 当前还剩多少钱
goodsCount 能买的各种商品数量是多少,list类型
'''
# 由于ans是函数外部定义的,内部如果要修改(只读取可以不加)则需要用global申明。
global ans
if step == len(prices):
if moneyLeft >= 0 and moneyLeft < ans:
ans = moneyLeft
return
# 每种商品的可以选择[0..goodscount[step]]件, 每种商品选择完成后,再比对还剩多少钱
for i in range(goodsCount[step] + 1):
dfs(prices, step + 1, moneyLeft - i * prices[step], goodsCount)
解法二,暴力递归法
此时换了个思路,剩余的钱最少,也即能买到的价值最大。 即用money能买到的价值最多的商品。
# 暴力递归解法
import sys
# 能买价值最多的东西
def dp(amount, prices):
# 小于等于0时,能买的最大价值为0
if amount <= 0:
return 0
res = -sys.maxsize - 1 # 定义最小值
for p in prices:
# 金额买不起时contine
if amount < p:
continue
# amount - p的钱 能买的最大价值
subValue = dp(amount - p, prices)
res = max(subValue + p, res)
return res if res != -sys.maxsize - 1 else 0
# 在此基础上可以加入备忘录来优化时间复杂度
注意,此方法得到的是买到的最大价值,求最少剩余钱数,还需要钱数减去最大价值。
解法三,动态规划。
暴力递归是至顶向下求解,而动态规划是从底向上的递推求解。
# dp迭代解法
def leastMoneyLeft2(amount, prices):
# dp[i] 表示金额i可以买到的最大价值
# dp[0]也对应一种状态,故对应amount时dp的长度应为amount + 1
dp = [0 for i in range(amount + 1)] # 因为要求解的是dp[amount],故list的长度为amount + 1
# base case
dp[0] = 0
for a in range(len(dp)):
for p in prices:
# 钱不够,买不起
if (a - p) < 0:
continue
dp[a] = max(dp[a], dp[a - p] + p)
return amount - dp[amount]