1. 简介

在本篇文章中,我们将学习如何使用贪心算法来求解一个经典问题:给定金额,求使用最少数量的硬币进行找零。这个问题也被称为找零问题(Change-Making Problem)

我们会从问题定义开始,接着介绍贪心算法的解决思路,并通过一个实际例子来演示算法的执行过程。然后给出算法的伪代码和时间复杂度分析,最后指出该方法的局限性,并提出替代方案。


2. 找零问题定义

我们被提供一个硬币面额数组 D = {d1, d2, ..., dm},其中每个面额可以无限使用。我们的目标是找出一个硬币组合数组 S,使得这些硬币的面额之和等于给定金额 n,且硬币数量最少。

举个实际例子:

假设我们手头有无限数量的面额为 D = {1, 2, 10} 的硬币,现在需要给顾客找零 n = 15 元,最少需要多少枚硬币?


3. 解决思路

贪心算法通过每一步选择当前可用的最大面额硬币来构造解。其核心思想是:

  • 从最大面额开始尝试,尽可能多地使用该面额;
  • 每次使用后,将总金额减去该面额;
  • 直到金额减为 0。

示例解析

我们用上述例子来演示算法的执行过程:

  1. 当前金额为 15,最大面额是 10,使用 1 枚,金额变为 5;
  2. 最大可用面额是 2,使用 2 枚,金额变为 1;
  3. 使用 1 枚 1 元硬币,金额归零。

最终硬币组合为 S = {10, 2, 2, 1},共 4 枚硬币。

greedyalgo min coin 3


4. 算法伪代码

algorithm findMinimumNumberOfCoins(D, m, n):
    // D:硬币面额数组
    // m:面额种类数
    // n:目标金额
    // S:返回的硬币组合数组

    Sort D in ascending order

    S = empty array

    for i from m - 1 downto 0:
        while n >= D[i]:
            add D[i] to S
            n = n - D[i]
        if n == 0:
            break

    return S

时间复杂度分析

  • 排序操作时间复杂度为 O(m log m)
  • 遍历和减金额的操作最多执行 n 次,因此时间复杂度为 O(n)
  • 总体时间复杂度为 O(n),因为 m 通常远小于 n

5. 局限性

⚠️ 贪心算法的一个重要局限性是:在某些面额组合下,它不能给出最优解

示例说明

假设面额为 D = {1, 6, 10},要找零 n = 13

  • 贪心算法会选择 10 + 1 + 1 + 1 = 13,共 4 枚硬币
  • 实际最优解是 6 + 6 + 1 = 13,只需 3 枚硬币

这是因为贪心算法每一步都只选择当前局部最优解,而不能回溯或综合考虑全局情况。

解决方案:在这种情况下,我们可以使用动态规划(Dynamic Programming)来获得全局最优解。


6. 小结

  • 贪心算法是一种简单高效的找零问题求解方法;
  • 在某些面额组合下,它能快速给出最优解;
  • 但在其他组合下,它可能给出非最优解;
  • 如果需要保证最优性,应使用动态规划方法。

贪心算法适合用于面额为标准货币单位(如人民币、美元等)的场景,比如在收银系统中实现快速找零。

如果你在实际项目中使用该算法,请务必确认面额组合是否满足贪心选择性质,否则建议使用动态规划。


原始标题:Greedy Algorithm to Find Minimum Number of Coins