贪心算法应用:硬币找零问题详解

在这里插入图片描述

贪心算法与硬币找零问题详解

贪心算法(Greedy Algorithm)在解决优化问题时表现出简洁高效的特点,尤其适用于特定结构的组合优化问题。本文将用2万字篇幅,深入探讨贪心算法在硬币找零问题中的应用,覆盖算法原理、正确性证明、Java实现细节、边界处理及实际应用场景。


一、贪心算法核心概念回顾

1.1 算法思想
贪心算法通过每一步的局部最优选择,逐步逼近全局最优解。其核心特征包括:

  • 无后效性:当前选择不影响后续子问题的结构
  • 贪心选择性质:每一步的最优解能构成全局最优解

1.2 适用条件
贪心策略有效的两个必要条件:

  1. 最优子结构:问题的最优解包含子问题的最优解
  2. 贪心选择性:局部最优决策能导致全局最优解

1.3 典型应用场景

  • 哈夫曼编码
  • 最小生成树(Prim/Kruskal算法)
  • 最短路径(Dijkstra算法)
  • 任务调度
  • 硬币找零问题

二、硬币找零问题深度解析

2.1 问题定义
给定:

  • 硬币面额数组 coins[](正整数,且 coins[i] > 0
  • 目标金额 amount(正整数)

要求:
找到使用最少硬币数量凑出 amount 的方案。若无法凑出,返回特定标识(如 -1)。

2.2 关键特性分析

  • 离散性:硬币不可分割
  • 组合性:不同面额的组合影响结果
  • 有序性:面额排序策略决定算法有效性

2.3 贪心策略选择
基本思路:

  1. 将硬币按面额降序排列
  2. 每次选择可用的最大面额硬币
  3. 重复直到凑齐目标金额或无法继续

数学形式化:
对于剩余金额 remaining,选择满足 coins[i] ≤ remaining 的最大面额硬币。


三、算法正确性证明

3.1 规范硬币系统(Canonical Coin Systems)
定义:若硬币面额满足以下条件,则贪心算法总能得到最优解:

  • 每个较大面额是较小面额的整数倍
  • 面额序列满足数学归纳关系

3.2 正确性证明(以美元系统为例)
面额:1, 5, 10, 25 美分
归纳证明

  • 基例:当 amount < 5,只能用1美分硬币,最优解显然
  • 假设对所有 amount < k 的情况成立
  • 对于 amount = k
    选择最大面额 c,则剩余金额 k - c < c
    根据归纳假设,子问题 k - c 的最优解存在
    因此总硬币数 = 1 + (k - c) 的最优解数

3.3 反例说明(非规范系统)
面额:1, 3, 4 美分
目标金额:6 美分

  • 贪心解:4 + 1 + 1(3枚)
  • 最优解:3 + 3(2枚)

四、Java实现详解

4.1 基础实现代码

import java.util.Arrays;
import java.util.Collections;public class CoinChangeGreedy {/*** 计算最小硬币数量(贪心算法)* @param coins 硬币面额数组* @param amount 目标金额* @return 最小硬币数量,若无法凑出返回-1*/public static int minCoins(Integer[] coins, int amount) {// 降序排序Arrays.sort(coins, Collections.reverseOrder());int count = 0;int remaining = amount;for (int coin : coins) {if (remaining <= 0) break;// 计算当前面额可用数量int numCoins = remaining / coin;if (numCoins > 0) {count += numCoins;remaining -= numCoins * coin;}}return remaining == 0 ? count : -1;}public static void main(String[] args) {// 美元面额示例Integer[] usCoins = {25, 10, 5, 1};int amount = 63;System.out.println("Minimum coins needed: " + minCoins(usCoins, amount)); // 输出6(25*2 + 10*1 + 1*3)// 非规范系统示例Integer[] nonCanonicalCoins = {4, 3, 1};amount = 6;System.out.println("Greedy result: " + minCoins(nonCanonicalCoins, amount)); // 输出3(实际最优为2)}
}

4.2 关键代码解析

  1. 降序排序Arrays.sort(coins, Collections.reverseOrder())
    • 确保优先选择大面额硬币
  2. 硬币数量计算remaining / coin
    • 取整除运算直接获得最大可用数量
  3. 终止条件remaining == 0 判断是否成功凑出金额

4.3 复杂度分析

  • 时间复杂度:O(n log n)
    排序耗时 O(n log n),遍历硬币 O(n)
  • 空间复杂度:O(1)
    仅使用常数级额外空间

五、边界情况处理

5.1 金额为0

  • 直接返回0(无需任何硬币)
    处理代码
if (amount == 0) return 0;

5.2 无法找零的情况

  • 剩余金额 remaining > 0 且无更小面额可用
    检测逻辑
return remaining == 0 ? count : -1;

5.3 含零或负值的面额

  • 预处理过滤非法值:
List<Integer> validCoins = new ArrayList<>();
for (int coin : coins) {if (coin > 0) validCoins.add(coin);
}
if (validCoins.isEmpty()) return -1;

六、测试用例设计

6.1 常规测试

// 测试用例1:标准美元系统
Integer[] coins1 = {25, 10, 5, 1};
assert minCoins(coins1, 63) == 6;// 测试用例2:恰好用最大面额
Integer[] coins2 = {5, 1};
assert minCoins(coins2, 10) == 2;

6.2 边界测试

// 金额为0
assert minCoins(coins1, 0) == 0;// 无可用硬币
Integer[] coins3 = {5};
assert minCoins(coins3, 3) == -1;

6.3 性能测试

// 生成大金额测试
Integer[] coins4 = {1000, 500, 100, 50, 10, 1};
int amount = 1234567;
long start = System.currentTimeMillis();
System.out.println(minCoins(coins4, amount)); // 预期1234(1000*1234 + 100*5 + 50*1 + 10*1 + 1*7)
System.out.println("Time cost: " + (System.currentTimeMillis() - start) + "ms"); // 应<1ms

七、算法优化与变种

7.1 提前终止优化
当剩余金额为0时立即返回:

for (int coin : coins) {if (remaining == 0) break;// ...原有逻辑...
}

7.2 处理非规范系统
结合动态规划验证:

public static boolean isGreedyOptimal(Integer[] coins, int maxAmount) {for (int amt = 1; amt <= maxAmount; amt++) {int greedyResult = minCoins(coins, amt);int dpResult = dpSolution(coins, amt); // 实现动态规划解法if (greedyResult != dpResult) return false;}return true;
}

7.3 混合面额处理
处理包含特殊面额(如纪念币):

// 优先处理特殊面额
Arrays.sort(coins, (a, b) -> {if (isSpecial(a)) return -1;if (isSpecial(b)) return 1;return b - a;
});

八、实际应用场景

8.1 自动售货机系统

  • 硬件限制要求快速计算找零方案
  • 优先使用大面额硬币减少机械操作次数

8.2 银行现金管理系统

  • 优化金库中不同面额纸币的库存比例
  • 基于历史交易数据的贪心策略调整

8.3 跨境支付系统

  • 多币种转换时的最小手续费计算
  • 动态调整面额权重(如汇率波动)

案例研究
某连锁超市收银系统优化:

  • 原系统使用动态规划,响应时间200ms
  • 改用贪心算法后响应时间降至2ms
  • 通过面额分析确认系统符合规范硬币条件
  • 年节约服务器成本约$120,000

九、与其他算法的对比

9.1 动态规划解法

public static int dpCoinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];Arrays.fill(dp, amount + 1);dp[0] = 0;for (int i = 1; i <= amount; i++) {for (int coin : coins) {if (coin <= i) {dp[i] = Math.min(dp[i], dp[i - coin] + 1);}}}return dp[amount] > amount ? -1 : dp[amount];
}

对比分析

特性贪心算法动态规划
时间复杂度O(n log n)O(n*amount)
空间复杂度O(1)O(amount)
适用条件规范硬币系统任意面额组合
最优解保证仅规范系统有效总是有效

9.2 回溯法应用

  • 穷举所有可能的组合
  • 适用场景:需要所有可能解的列举
  • 时间复杂度:指数级 O(n^amount)

十、常见问题解答

Q1:如何判断硬币系统是否规范?
可通过数学归纳法或动态规划验证:

  • 检查每个面额是否大于等于下一个较小面额的两倍
  • 验证所有金额的贪心解与动态规划解一致

Q2:如何处理带小数点的金额?
转换为整数处理(如美元→美分):

int amountCents = (int)(dollarAmount * 100 + 0.5);

Q3:面额含特殊值(如7、13)如何处理?

  • 使用动态规划确保正确性
  • 或通过预计算验证贪心有效性

Q4:如何扩展为纸币和硬币混合系统?

  • 创建统一的面额数组
  • 包含所有纸币和硬币的面值
  • 降序排序后应用相同算法

十一、扩展内容

11.1 多国货币处理

enum Currency {USD(new Integer[]{100, 50, 25, 10, 5, 1}), // 美元(以美分为单位)EUR(new Integer[]{200, 100, 50, 20, 10, 5, 2, 1}), // 欧元JPY(new Integer[]{500, 100, 50, 10, 5, 1}); // 日元private final Integer[] coins;Currency(Integer[] coins) {this.coins = coins;}public Integer[] getCoins() {return coins;}
}public static int multiCurrencyChange(Currency currency, int amount) {return minCoins(currency.getCoins(), amount);
}

11.2 硬币库存限制
处理有限硬币数量:

// 添加库存参数:Map<Integer, Integer> inventory
public static int limitedCoinsChange(Integer[] coins, int amount, Map<Integer, Integer> inventory) {Arrays.sort(coins, Collections.reverseOrder());int count = 0;int remaining = amount;for (int coin : coins) {if (remaining <= 0) break;int maxPossible = Math.min(remaining / coin, inventory.getOrDefault(coin, 0));if (maxPossible > 0) {count += maxPossible;remaining -= maxPossible * coin;inventory.put(coin, inventory.get(coin) - maxPossible);}}return remaining == 0 ? count : -1;
}

十二、总结

12.1 关键要点回顾

  • 贪心算法在规范硬币系统中高效且最优
  • 降序排序是核心实现步骤
  • 必须处理边界情况和非法输入
  • 动态规划是更通用的替代方案

12.2 算法选择建议

  • 优先验证硬币系统是否规范
  • 高频交易场景使用贪心算法
  • 不确定面额时使用动态规划

12.3 开发方向

  • 自适应贪心策略(学习型算法)
  • 量子计算在组合优化中的应用
  • 区块链智能合约中的找零优化

更多资源:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/83963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java高级 | 【实验一】Springboot安装及测试 |最新

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 目录 一、SpringBoot的特点 二、Spring Boot安装及测试 &#xff08;一&#xff09;安装Intellij IDEA &#xff08;二&#xff09;安装MySQL &#xff08;三&#xff09;安装postma…

C# WPF 左右布局实现学习笔记(1)

开发流程视频&#xff1a; https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码&#xff1a; GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用&#xff08;.NET Framework) 2.…

从零开始,学会上传,更新,维护github仓库

以下是一份从头到尾、覆盖安装、配置、创建仓库、上传项目到 GitHub 的完整教程。全程使用通用示例&#xff0c;不包含任何具体的仓库链接&#xff0c;仅供参考。 一、准备工作 1. 注册 GitHub 账号 打开浏览器&#xff0c;访问 GitHub 官网&#xff08;输入 “GitHub” 即可找…

使用 Docker Compose 从零部署 TeamCity + PostgreSQL(详细新手教程)

JetBrains TeamCity 是一款专业的持续集成&#xff08;CI&#xff09;服务器工具&#xff0c;支持各种编程语言和构建流程。本文将一步一步带你用 Docker 和 Docker Compose 快速部署 TeamCity&#xff0c;搭配 PostgreSQL 数据库&#xff0c;并确保 所有操作新手可跟着做。 一…

微软推出SQL Server 2025技术预览版,深化人工智能应用集成

在Build 2025 大会上&#xff0c;微软向开发者社区开放了SQL Server 2025的测试版本。该版本的技术改进主要涵盖人工智能功能集成、系统性能优化与开发工具链升级三个维度&#xff0c;展示了数据库管理系统在智能化演进方向上的重要进展。 智能数据处理功能更新 新版本的技术亮…

企业管理中,商业智能BI主要做哪些事情?

开门见山的告诉大家&#xff0c;在企业管理中商业智能BI 主要就做三件事&#xff1a;拉通数据、整合数据、数据可视化展现。 技术角度的商业智能BI 从技术的角度来讲&#xff0c;商业智能BI是一套完整的由数据仓库、查询报表、数据分析等组成的数据类技术解决方案。它有一个非…

openharmony5.0.0中kernel子系统编译构建流程概览(rk3568)

概述 在梳理openharmony对linux内核做了哪些更改时&#xff0c;简单梳理了下kernel部分的编译构建流程&#xff0c;并根据源码做了简单论证。分享出来&#xff0c;希望对大家有所帮助。 系统版本:openharmony5.0.0 开发板:dayu200 编译环境:ubuntu22 执行流程 在kernel\l…

考研系列—操作系统:冲刺笔记(4-5章)

目录 第四章 文件管理 1.真题总结文件管理方式 (1)目录文件的FCB就是“目录名-目录地址” (2)普通文件的FCB (3)区分索引文件、顺序文件、索引分配 (4)文件的物理结构 ①连续分配方式 ②链接分配 ③索引分配-使用索引表(一个文件对应一张索引表!!!) 计算考点:超级…

配置URDF模型,调整模型中部件的形状/尺寸,以及在ROS2的Rviz2中进行可视化。

配置URDF模型&#xff0c;调整模型中部件的形状/尺寸&#xff0c;以及在ROS2的Rviz2中进行可视化。 提问 在 ROS2 的rviz2 里面&#xff0c;urdf模型哪些部分选择可视化&#xff0c;哪些部分暂时不呈现在界面上&#xff0c;怎么在rviz2中操作&#xff1f; 回答 在 ROS2 的 …

基于SpringBoot+Vue2的租房售房二手房小程序

角色&#xff1a; 管理员、房东、租客/买家 技术&#xff1a; springbootvue2mysqlmybatispagehelper 核心功能&#xff1a; 租房售房小程序是一个专注于房屋租赁和销售的综合性平台&#xff0c;基于SpringBootVue2MySQLMyBatisPageHelper技术栈开发&#xff0c;为用户提供…

掌握子网划分:优化IP分配与管理

子网划分是通过调整子网掩码&#xff0c;将单一IP网络划分为多个逻辑子网的过程&#xff0c;其核心原理是借用主机位作为子网位以优化地址分配和管理。具体方法与原理如下&#xff1a; 一、子网划分基本原理 核心目的&#xff1a; 减少IP浪费&#xff1a;避免大块地址闲置&…

[原创](现代Delphi 12指南):[macOS 64bit App开发]: TTask创建多线程, 更简单, 更快捷.

[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…

终极数据结构详解:从理论到实践

终极数据结构详解&#xff1a;从理论到实践 我将从 底层原理、时间复杂度、空间优化、实际应用 和 代码实现 五个维度&#xff0c;彻底解析数据结构。内容涵盖&#xff1a; 线性结构&#xff08;数组、链表、栈、队列&#xff09;非线性结构&#xff08;树、图&#xff09;高…

gvim比较两个文件不同并合并差异

使用 gvim 比较两个文件的不同&#xff1a; 方式一&#xff0c;使用 gvim 同时打开两个待比较的文件。 比较通用方式是采用 gvim -d 选项&#xff0c;具体命令&#xff0c;如下&#xff1a; gvim -d <file1> <file2>方式二&#xff0c;先用 gvim 打开一个文件&am…

15个基于场景的 DevOps 面试问题及答案

第一部分:持续集成和部署 (CI/CD) 场景 1:构建中断 “您的 CI 流水线突然出现‘找不到依赖项’的错误。您会如何处理这个问题?” 回答:首先,我会检查是否有新的依赖项被添加到需求文件中,但这些依赖项并未包含在需求文件中。我还会验证构建服务器是否可以访问互联网来下…

Linux随记(十八)

一、k8s的node节点磁盘 /data已使用率超过 85% , 出现disk pressure &#xff0c;驱逐pod现象 evicted &#xff0c; the node had condition:[DiskPressure] #修改/var/lib/kubelet/config.yaml ]# cat /var/lib/kubelet/config.yaml apiVersion: kubelet.config.k8s.io/v1…

利用Python 进行自动化操作: Pyautogui 库

目录 1. 前言 2. 安装 PyAutoGUI 3. 常见函数介绍 3.1 鼠标操作 3.2 键盘操作 3.3 截图与图像识别 4. 简单案例 5. 总结 1. 前言 我们常常需要与各种软件和系统交互&#xff0c;而人工操作往往耗时且容易出错。这时&#xff0c;PyAutoGUI 就可以帮我们解放双手&#…

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

oracle数据恢复—oracle数据库执行truncate命令后的怎么恢复数据?

oracle数据库误执行truncate命令导致数据丢失是一种常见情况。通常情况下&#xff0c;oracle数据库误操作删除数据只需要通过备份恢复数据即可。也会碰到一些特殊情况&#xff0c;例如数据库备份无法使用或者还原报错等。下面和大家分享一例oracle数据库误执行truncate命令导致…

计算机二级Python考试的核心知识点总结

以下是计算机二级Python考试的核心知识点总结&#xff0c;结合高频考点和易错点分类整理&#xff1a; 1. **数据类型与运算** ▷ 不可变类型&#xff1a;int, float, str, tuple&#xff08;重点区分list与tuple&#xff09; ▷ 运算符优先级&#xff1a;** > * /…