贪心算法之找零钱问题详细解读(附带Java代码解读)

news/2024/12/22 21:48:11/

找零钱问题(Change-making Problem)是计算机科学中一个经典的问题,特别是在算法>贪心算法的背景下非常常见。问题可以简单描述为:

给定一个目标金额和若干种不同面额的硬币,如何使用最少数量的硬币组合来凑齐目标金额。

找零钱问题的形式化描述:
  • 输入:一个目标金额 MMM 和一个硬币面额数组 {c1,c2,...,cn}\{c_1, c_2, ..., c_n\}{c1​,c2​,...,cn​},数组中的每个元素代表硬币的面值,面额通常是正整数。
  • 输出:使用的最少硬币数量以及每种面额硬币的数量,使得硬币面值的总和等于目标金额 MMM。

目标:我们需要找出一组硬币组合,使得这些硬币的总金额恰好为 MMM,并且使用的硬币数量尽可能少。

示例:

假设我们有硬币面额:{1,5,10,25}\{1, 5, 10, 25\}{1,5,10,25},需要找零的金额为63。 我们期望得到一种使用最少硬币数的方案,像这样:

  • 2 枚 25分硬币
  • 1 枚 10分硬币
  • 3 枚 1分硬币

总共用6枚硬币构成63。

算法>贪心算法的找零钱问题

算法>贪心算法(Greedy Algorithm)是一种逐步求解问题的策略。每一步都做出当前最优的选择(局部最优解),期望通过这种方式最终得到全局最优解。

在找零钱问题中,算法>贪心算法的核心思想是:

  1. 优先选择面额较大的硬币:尽可能多地使用面额最大的硬币,这样可以在每一步减少目标金额。
  2. 依次尝试较小面额的硬币:在剩余的金额上,继续选择次大面额的硬币,直到总金额达到目标。
算法>贪心算法的步骤:
  1. 降序排列硬币面额:保证从面额最大的硬币开始选择。
  2. 计算使用某种硬币的数量:对于当前面额 cic_ici​,尽量多地使用该硬币,即计算 需要的硬币数量=剩余金额/ci\text{需要的硬币数量} = \text{剩余金额} / c_i需要的硬币数量=剩余金额/ci​。
  3. 更新剩余金额:剩余金额等于当前金额对硬币面额的取余,即 剩余金额=剩余金额%ci\text{剩余金额} = \text{剩余金额} \% c_i剩余金额=剩余金额%ci​。
  4. 重复步骤2和3:依次对较小面额的硬币重复上述过程,直到剩余金额为0。

算法>贪心算法的适用性

虽然算法>贪心算法在许多情况下都能得到最优解,但并不是所有硬币组合下都能使用算法>贪心算法。例如:

  • 当硬币的面额为 {1,3,4}\{1, 3, 4\}{1,3,4} 时,找零金额为 6 的贪心策略会先选择一个面额为4的硬币,剩下的金额是2。这时再选择两个1分硬币,总共用了3枚硬币。
  • 但实际最优解是选择两个3分硬币,总共用2枚硬币。

因此,算法>贪心算法对于某些特定的硬币面额集合可能并不能保证最优解。但对于标准的货币系统(如1元、5元、10元等),贪心策略能提供最优解。

算法>贪心算法 vs 动态规划

对于找零钱问题,**动态规划(Dynamic Programming)**是另一种通用的解法。动态规划通过逐步构建最优子问题的解来获得全局最优解,能够保证在所有硬币组合下找到最优解。它的基本思想是:

  1. 通过递归或自底向上的方式,计算找零金额为 MMM 时最少需要多少硬币。
  2. 通过保存子问题的解,避免重复计算。

动态规划相比算法>贪心算法,适用范围更广,但时间复杂度较高,尤其在问题规模较大的情况下。

算法>贪心算法的优缺点

优点:
  • 简单直观:每次选择当前最优解,易于实现。
  • 效率高:由于不需要考虑全局最优,只需要每次做局部最优选择,因此它的时间复杂度通常较低(例如 O(n)O(n)O(n))。
  • 适用于部分特殊问题:如硬币面额是标准货币系统时,贪心策略能找到最优解。
缺点:
  • 可能得不到全局最优解算法>贪心算法基于局部最优选择,无法保证全局最优,特别是在面额设计复杂的情况下。
  • 受限于问题结构算法>贪心算法不是所有问题的通用解法,需要问题具有贪心选择性质,才能使用该方法。

Java代码详细解读

java">import java.util.Arrays;public class GreedyChange {// 算法>贪心算法实现找零钱问题public static void findChange(int[] coins, int amount) {// 对硬币面额进行升序排序Arrays.sort(coins);  // Arrays.sort会将数组从小到大排序// 由于我们要从大面额开始,后面会逆序遍历int[] result = new int[coins.length]; // 结果数组,用来记录每种面额使用的硬币数量// 逆序遍历硬币面额,从最大面额的硬币开始for (int i = coins.length - 1; i >= 0; i--) {// 如果当前金额大于等于当前硬币面额,则使用该面额硬币if (amount >= coins[i]) {result[i] = amount / coins[i]; // 计算当前面额硬币的数量amount = amount % coins[i];    // 更新剩余金额}}// 输出结果:展示每种面额的硬币使用数量System.out.println("所需硬币的最少数量是:");for (int i = coins.length - 1; i >= 0; i--) {if (result[i] != 0) {System.out.println("面额 " + coins[i] + " 的硬币: " + result[i] + " 枚");}}// 如果还有剩余金额未能找零,说明无法精确找零if (amount > 0) {System.out.println("无法精确找零,剩余金额: " + amount);}}public static void main(String[] args) {// 定义硬币面额数组int[] coins = {1, 5, 10, 25}; // 硬币面额// 定义需要找零的金额int amount = 63; // 需要找零的金额// 调用找零钱方法findChange(coins, amount); }
}
代码详细解读:
  1. Arrays.sort(coins):对硬币面额数组进行排序,以确保我们可以从最大面额硬币开始选择。由于 Arrays.sort() 默认是升序排序,所以我们在遍历时使用逆序(从最大到最小)。

  2. result[i] = amount / coins[i]:这是计算当前剩余金额可以使用多少枚面额为 coins[i] 的硬币。这个值直接表示我们可以使用多少个该面额的硬币。

  3. amount = amount % coins[i]:计算剩余需要找零的金额。通过模运算(%),我们能够得到在使用该面额硬币之后,剩余的找零金额。

  4. if (result[i] != 0):该条件用于过滤掉那些没有使用的面额硬币,只输出使用到的硬币。

  5. if (amount > 0):在结束硬币选择之后,如果金额还大于0,意味着无法完全找零,比如在某些情况下没有1分硬币的情况下,可能会有无法精确找零的情况。

结论

通过算法>贪心算法解决找零钱问题,我们能够快速获得一个使用最少硬币的解,适合大部分标准货币系统。在实际应用中,可以根据硬币面额系统和问题的具体特点,选择算法>贪心算法或动态规划等更加普适的算法


http://www.ppmy.cn/news/1525386.html

相关文章

电力系统调度控制台的功能有哪些

在复杂多变的现代电力系统中,调度控制台作为其核心管理与控制的中枢,扮演着不可或缺的角色。它不仅是确保电网安全稳定运行的关键,也是实现电力资源高效配置的重要工具。那么,电力系统调度控制台究竟具备哪些关键功能呢? 首先&am…

Kubernetes 与 springboot集成

Kubernetes 与 Spring Boot 集成详解 Kubernetes(简称 K8s)是一个用于自动化部署、扩展和管理容器化应用的开源平台,而 Spring Boot 是 Java 开发领域中非常流行的微服务框架。将这两者结合,可以充分利用 Kubernetes 强大的容器编…

【Linux 从基础到进阶】Docker 容器技术基础与应用

Docker 容器技术基础与应用 Docker 是一种开源的容器化平台,它使得开发人员能够自动化应用程序的部署、管理和隔离。通过容器技术,Docker 提供了一种轻量级的虚拟化解决方案,与传统的虚拟机相比,容器的启动速度更快,占用资源更少,因此广泛应用于现代 DevOps 流程和微服务…

类和对象(中)【上篇】(构造,析构,拷贝函数)

🌟个人主页:落叶 目录 类的默认成员函数 构造函数 无参构造 带参构造函数 全缺省构造函数 析构函数 对⽐C和C解决括号匹配问题 C语言版的Stack C版的Stack 拷⻉构造函数 类的默认成员函数 默认成员函数就是⽤⼾没有显式实现,编译器会…

《JavaEE进阶》----11.<SpringIOCDI【Spring容器+IOC详解+DI介绍】>

本篇博客会详细讲解什么是Spring。 SpringIOC SpringID 五个类注解:Controller、Service、Repository、Component、Configuration 一个方法注解:Bean 什么是Spring IOC容器 Spring 是包含众多工具的IOC容器。能装东西的容器。 1.容器 如我们之前学的 Tom…

ASP.net core 8.0网站发布

安装 net8 下载地址:https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0 复制wwwroot文件夹 把项目中的wwwroot文件夹复制到项目文件夹下bin\Debug\net8.0下,这文件夹就是可运行的网站程序 运行 运行bin\Debug\net8.0文件夹下的exe程序即可

如何使用极狐GitLab 实现 GitOps?

极狐GitLab 是 GitLab 在中国的发行版,专门面向中国程序员和企业提供企业级一体化 DevOps 平台,用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规,而且所有的操作都是在一个平台上进行,省事省心省钱。可以一键安装极狐GitL…

实现一个点缓慢到达另一个点

实现一个点缓慢到达另一个点 使用线性插值的方法将点从一个位置平滑地移动到另一个位置: #include <stdio.h> #include <stdlib.h> #include <unistd.h> // 用于 sleep 函数// 点的结构体