剩余最少的钱

有一批价格分别为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]
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值