You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非足额硬币组合下支付金额的最小损失求解算法问询

关于非标准硬币系统下最小损失支付的解法

这是个非常典型的硬币组合优化问题!先给你理清楚前因后果和可行的解法:

先聊聊你提到的「简单方法」

你一开始写的代码是贪心算法的实现:

for i, j in zip(coins, needed):
    if amount >= 2*i:
        j = amount // i
        amount = amount - j * i
    print(i ," : ", j)

这种方法只在「canonical硬币系统」下有效——也就是像美元常用的1、5、10、50这类面额,贪心选最大面额能得到最优解。但一旦硬币系统不是canonical(比如你举的100、50、20),贪心就彻底失效了:比如要付98美元时,贪心会先选50(因为98<100),剩下48再选两个20,最后剩8凑不出来,连可行解都找不到,更别说最优的「付100亏2」了。

那通用解法是什么?

你的核心需求是:找到≥目标金额的最小支付额(即损失最小),同时这个支付额能被给定的硬币面额凑出来。这里有两种主流思路,取决于你的问题规模:

1. 暴力枚举(适合极小规模场景)

如果硬币面额数量很少、目标金额不大,可以直接枚举所有可能的硬币组合,计算对应的支付额,筛选出≥目标且损失最小的组合。但这种方法时间复杂度极高,面额多一点就会慢到没法用,只能当玩具方案。

2. 动态规划(通用高效解法)

这是处理这类组合优化问题的标准方案,能覆盖所有硬币系统的情况。我们可以通过动态规划数组记录「凑出某金额所需的最小硬币数」,然后在≥目标金额的范围内找损失最小的可行金额。

举个针对你例子的Python实现:

def find_min_loss_payment(target, coins):
    # 设定上限:目标金额+最大面额,超过这个的话肯定有更小损失的方案
    max_possible = target + max(coins)
    # dp[x]表示凑出x元需要的最少硬币数,初始设为无穷大(不可行)
    dp = [float('inf')] * (max_possible + 1)
    dp[0] = 0  # 凑0元需要0个硬币

    # 填充dp数组
    for amount in range(1, max_possible + 1):
        for coin in coins:
            if amount >= coin and dp[amount - coin] + 1 < dp[amount]:
                dp[amount] = dp[amount - coin] + 1

    # 找损失最小的可行支付额
    min_loss = float('inf')
    best_payment = None
    for amount in range(target, max_possible + 1):
        if dp[amount] != float('inf'):
            current_loss = amount - target
            if current_loss < min_loss:
                min_loss = current_loss
                best_payment = amount

    if not best_payment:
        return None  # 理论上只要有大面额就不会出现这种情况

    # 回溯找用了哪些硬币
    coins_used = {}
    remaining = best_payment
    while remaining > 0:
        for coin in coins:
            if remaining >= coin and dp[remaining - coin] == dp[remaining] - 1:
                coins_used[coin] = coins_used.get(coin, 0) + 1
                remaining -= coin
                break

    return coins_used, min_loss

# 测试你的例子
target = 98
coins = [100, 50, 20]
result = find_min_loss_payment(target, coins)
if result:
    used, loss = result
    print(f"最优方案:支付{sum(k*v for k,v in used.items())}美元({used}),损失{loss}美元")

运行后会输出:最优方案:支付100美元({100: 1}),损失2美元,正好是你想要的结果。

总结一下

  • 贪心算法只在特定的「标准硬币系统」下有用,非标准系统直接放弃;
  • 动态规划是通用解法,能高效找到最小损失的支付组合;
  • 暴力枚举只适合极小规模的场景,实际开发中几乎不用。

内容的提问来源于stack exchange,提问作者sboda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:26:43