# 设计一个 ATM 机器(LeetCode 第2241题)

设计一个 ATM 机器(LeetCode 第2241题)

在本篇博客中,我们将深入探讨如何设计一个 ATM 机器,以满足存取钞票的需求。这个问题来源于 LeetCode 的第2241题,旨在考察如何高效地管理不同面额的钞票并处理用户的存取请求。

问题描述

背景

设计一个 ATM 机器,该机器存有 5 种面值的钞票:$20、$50、$100、$200 和 $500 美元。初始时,ATM 机是空的。用户可以通过该机器进行存款或取款操作。

取款规则

  • 优先取出大面额钞票:在取款时,ATM 会优先取出较大面额的钞票,以减少取款的张数。
  • 精确取款:如果无法通过可用的钞票组合精确取出所需金额,则取款请求将被拒绝。

具体要求

请实现一个 ATM 类,支持以下操作:

  • ATM(): 初始化 ATM 对象。
  • deposit(int[] banknotesCount): 存入 5 种面额的钞票数量,依次为 $20、$50、$100、$200 和 $500。
  • withdraw(int amount): 尝试取出指定金额的钞票,返回一个长度为 5 的数组,表示每种面额钞票的数量。如果无法完成取款,则返回 [-1]

示例

输入:
["ATM", "deposit", "withdraw", "deposit", "withdraw", "withdraw"]
[[], [[0,0,1,2,1]], [600], [[0,1,0,1,1]], [600], [550]]
输出:
[null, null, [0,0,1,0,1], null, [-1], [0,1,0,0,1]]

解释

ATM atm = new ATM();
atm.deposit([0,0,1,2,1]); // 存入 1 张 $1002 张 $2001 张 $500 的钞票。
atm.withdraw(600);        // 返回 [0,0,1,0,1]。机器取出 1 张 $1001 张 $500 的钞票。
atm.deposit([0,1,0,1,1]); // 存入 1 张 $501 张 $2001 张 $500 的钞票。
atm.withdraw(600);        // 返回 [-1]。无法精确取出 600 美元。
atm.withdraw(550);        // 返回 [0,1,0,0,1],取出 1 张 $501 张 $500 的钞票。

解决方案

数据结构选择

为了高效管理钞票,我们使用一个数组来表示不同面额钞票的数量。具体来说:

  • 定义钞票面额数组 DENOMINATIONS = [20, 50, 100, 200, 500]
  • 使用数组 banknotes 来存储每种面额的钞票数量,索引与 DENOMINATIONS 对应。

操作实现

存款操作 (deposit)

存款操作相对简单,只需将输入数组中的钞票数量累加到 banknotes 对应的位置。

取款操作 (withdraw)

取款操作需要考虑以下几点:

  1. 优先取出大面额钞票:从高到低遍历 DENOMINATIONS,尽可能多地取出当前面额的钞票。
  2. 确保取款金额精确:在取出钞票的过程中,需实时更新剩余金额,并确保最终能精确取出所需金额。
  3. 回滚机制:如果在取款过程中发现无法精确取出所需金额,应回滚所有取出的钞票,确保 ATM 状态不变。

代码实现

以下是基于上述思路的 Python 实现:

from typing import List# 定义钞票面额
DENOMINATIONS = [20, 50, 100, 200, 500]
KINDS = len(DENOMINATIONS)class ATM:def __init__(self):# 初始化每种面额的钞票数量为0self.banknotes = [0] * KINDSdef deposit(self, banknotesCount: List[int]) -> None:"""存入钞票,banknotesCount按面额顺序依次对应20, 50, 100, 200, 500"""for i, count in enumerate(banknotesCount):self.banknotes[i] += countprint(f"存款后钞票状态: {self.banknotes}")def withdraw(self, amount: int) -> List[int]:"""尝试取出指定金额的钞票,返回取出各面额的数量数组或[-1]表示失败"""ans = [0] * KINDSremaining = amount# 从大面额开始取for i in range(KINDS - 1, -1, -1):if remaining >= DENOMINATIONS[i]:# 计算当前面额最多可取出的钞票数量max_can_take = remaining // DENOMINATIONS[i]take = min(max_can_take, self.banknotes[i])ans[i] = takeremaining -= take * DENOMINATIONS[i]print(f"取出 {DENOMINATIONS[i]} 美元: {ans[i]} 张, 剩余金额: {remaining}")# 如果无法精确取出所需金额,返回 [-1]if remaining != 0:print(f"取款失败,无法取出 {amount} 美元")return [-1]# 更新钞票数量for i in range(KINDS):self.banknotes[i] -= ans[i]print(f"取款后钞票状态: {self.banknotes}")return ans

代码解析

  1. 初始化

    def __init__(self):self.banknotes = [0] * KINDS
    

    初始化时,ATM 中所有面额的钞票数量均为0。

  2. 存款操作

    def deposit(self, banknotesCount: List[int]) -> None:for i, count in enumerate(banknotesCount):self.banknotes[i] += countprint(f"存款后钞票状态: {self.banknotes}")
    

    遍历输入的 banknotesCount 数组,将每种面额的钞票数量累加到 self.banknotes 中。

  3. 取款操作

    def withdraw(self, amount: int) -> List[int]:ans = [0] * KINDSremaining = amountfor i in range(KINDS - 1, -1, -1):if remaining >= DENOMINATIONS[i]:max_can_take = remaining // DENOMINATIONS[i]take = min(max_can_take, self.banknotes[i])ans[i] = takeremaining -= take * DENOMINATIONS[i]print(f"取出 {DENOMINATIONS[i]} 美元: {ans[i]} 张, 剩余金额: {remaining}")if remaining != 0:print(f"取款失败,无法取出 {amount} 美元")return [-1]for i in range(KINDS):self.banknotes[i] -= ans[i]print(f"取款后钞票状态: {self.banknotes}")return ans
    
    • 初始化ans 用于记录每种面额取出的钞票数量,remaining 用于跟踪剩余金额。
    • 取钞逻辑:从大到小遍历面额,尽可能多地取出当前面额的钞票,同时更新剩余金额。
    • 失败检查:如果在遍历结束后,remaining 不为0,表示无法精确取出所需金额,返回 [-1]
    • 更新钞票数量:如果取款成功,更新 self.banknotes 中的钞票数量。
    • 返回结果:返回 ans 数组,表示取出的各面额钞票数量。

运行示例

以下是一个运行示例,展示了如何使用 ATM 类:

# 初始化ATM
atm = ATM()# 存入1张$100,2张$200,1张$500
atm.deposit([0, 0, 1, 2, 1])  # 存款后钞票状态: [0, 0, 1, 2, 1]# 取出600美元
print(atm.withdraw(600))      # 输出: [0,0,1,0,1], 取款后钞票状态: [0,0,0,2,0]# 存入1张$50,1张$200,1张$500
atm.deposit([0, 1, 0, 1, 1])  # 存款后钞票状态: [0,1,0,3,1]# 尝试取出600美元
print(atm.withdraw(600))      # 输出: [-1], 取款失败,无法取出600美元# 取出550美元
print(atm.withdraw(550))      # 输出: [0,1,0,0,1], 取款后钞票状态: [0,0,0,3,0]

输出结果

存款后钞票状态: [0, 0, 1, 2, 1]
取出 500 美元: 1 张, 剩余金额: 100
取出 200 美元: 0 张, 剩余金额: 100
取出 100 美元: 1 张, 剩余金额: 0
取出 50 美元: 0 张, 剩余金额: 0
取出 20 美元: 0 张, 剩余金额: 0
取款后钞票状态: [0, 0, 0, 2, 0]
[0, 0, 1, 0, 1]
存款后钞票状态: [0, 1, 0, 3, 1]
取出 500 美元: 1 张, 剩余金额: 100
取出 200 美元: 0 张, 剩余金额: 100
取出 100 美元: 0 张, 剩余金额: 100
取出 50 美元: 0 张, 剩余金额: 100
取出 20 美元: 0 张, 剩余金额: 100
取款失败,无法取出 600 美元
[-1]
取出 500 美元: 1 张, 剩余金额: 50
取出 200 美元: 0 张, 剩余金额: 50
取出 100 美元: 0 张, 剩余金额: 50
取出 50 美元: 1 张, 剩余金额: 0
取出 20 美元: 0 张, 剩余金额: 0
取款后钞票状态: [0, 0, 0, 3, 0]
[0, 1, 0, 0, 1]

复杂度分析

  • 时间复杂度

    • deposit 操作的时间复杂度为 O(1),因为面额种类固定为5。
    • withdraw 操作的时间复杂度同样为 O(1)。
  • 空间复杂度

    • 使用了固定大小的数组 banknotesans,空间复杂度为 O(1)。

总结

通过使用贪心算法,我们能够有效地管理 ATM 机器中的钞票,并满足用户的存取需求。然而,需要注意的是,贪心算法在某些情况下可能无法满足取款金额的精确要求。因此,在实现时,我们需要在取款后进行剩余金额的检查,以确保取款的合法性和准确性。

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

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

相关文章

操作系统复习(理论版)

目录 只会在选择填空出现类型 第一章:操作系统导论 操作系统介绍 不得不知道的概念 可能出现在答题的类型 第二章:进程调度 进程管理: 处理机调度: 进程同步: 死锁: 预防死锁: 避免死…

概述(讲讲python基本语法和第三方库)

我是北子,这是我自己写的python教程,主要是记录自己的学习成果方便自己日后复习, 我先学了C/C,所以这套教程中可能会将很多概念和C/C去对比,所以该教程大概不适合零基础的人。 it seems that python nowadays 只在人工…

Linux(Centos 7.6)命令详解:ls

1.命令作用 列出目录内容(list directory contents) 2.命令语法 Usage: ls [OPTION]... [FILE]... 3.参数详解 OPTION: -l,long list 使用长列表格式-a,all 不忽略.开头的条目(打印所有条目,包括.开头的隐藏条目&#xff09…

详解MySQL SQL删除(超详,7K,含实例与分析)

文章目录 前言1. 删除表中的所有记录基本语法使用场景注意事项运用实例分析说明2. 删除特定记录基本语法使用场景注意事项运用实例分析说明3. 删除单条记录基本语法使用场景注意事项运用实例分析说明4. 删除违反引用完整性的记录基本语法使用场景注意事项运用实例分析说明5. 删…

[图形渲染] 【Unity】UnityShader操作基础6-OpenGL与DirectX中的纹理坐标差异及语法差异分析

在现代图形编程中,OpenGL和DirectX作为两大主流图形API,各自有其独特的处理方式和语法规范。尽管它们的功能和目的相似,但在某些细节上却存在显著差异,尤其是在纹理坐标和着色器编程的相关操作上。本文将探讨OpenGL与DirectX在纹理坐标、语法以及语义上的差异,并给出一些跨…

numpy数组学习

前言 有时看到数组,容易被绕晕,不知道每个列表的数字分别代表什么,因此记录下来,用于后续参考。 1.轮廓点坐标 (n, 1, 2) 格式解释 (n, 1, 2): n:表示轮廓上的点数。每个点都是轮廓的一部分。 1&#x…

改善 Kibana 中的 ES|QL 编辑器体验

作者:来自 Elastic Marco Liberati 随着新的 ES|QL 语言正式发布,Kibana 中开发了一种新的编辑器体验,以帮助用户编写更快、更好的查询。实时验证、改进的自动完成和快速修复等功能将简化 ES|QL 体验。 我们将介绍改进 Kibana 中 ES|QL 编辑器…

Mono里运行C#脚本25—mono_codegen

前面分析怎么样找到主函数Main的入口点功能,也就是说已经找到了这个函数的CIL代码。虽然找到了代码,但是还不能执行它的,因为它是一种虚拟机的代码。也就是说它是假的代码,不是现实世界存在的机器的代码,因此不能直接执行,必须经过后端编译器的再次编译才能真正运行它。下…

基于Spring Boot的紧急物资管理系统

基于Spring Boot的紧急物资管理系统是一个非常实用的应用,特别是在应对自然灾害、公共卫生事件等情况下。该系统可以帮助管理者有效地追踪和分配物资,确保资源能够及时到达需要的地方。以下是一个基本的实现思路和一些关键组件: 项目规划 需…

mini-dog-c编译器之词法分析

mini-dog-c 是一个小型的 C 语言编译器,是我学习和理解编译器基本工作原理的实践项目。其词法分析器能够识别 C 语言的基本语法元素,包括常见的标识符、整数和浮点数字面量、布尔字面量以及字符串字面量。此外,它还支持基本的算术和逻辑操作符…

机器学习基础-概率图模型

(一阶)马尔科夫模型的基本概念 状态、状态转换概率、初始概率 状态转移矩阵的基本概念 隐马尔可夫模型(HMM)的基本概念 条件随机场(CRF)的基本概念 实际应用中的马尔科夫性 自然语言处理: 在词…

力扣hot100——链表

160. 相交链表 class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {set<ListNode*> s;ListNode* h headA;while (h ! NULL) {s.insert(h);h h->next;}h headB;while (h ! NULL){if (s.find(h) ! s.end()) {return h;}h …

Qt打包为exe文件

个人学习笔记 选择release 进入项目文件夹&#xff0c;查看releas生成的文件 releas文件路径 进入release看到exe文件&#xff0c;但是无法执行 将exe文件单独放到一个文件夹内 选择MinGW 用CD 进入存放exe文件的路径&#xff0c;输入下面指令 cd J:\C\Qt\test4-3-1 windeploy…

VScode怎么重启

原文链接&#xff1a;【vscode】vscode重新启动 键盘按下 Ctrl Shift p 打开命令行&#xff0c;如下图&#xff1a; 输入Reload Window&#xff0c;如下图&#xff1a;

小屏幕下通过css自动实现上下位置颠倒例子

<!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>Demo</title><!-- 请勿在项目正式环境中引用该 layui.css 地址 --…

下列指标组合中,不能用于系统性评价缺陷识别模型精度的指标

19 下列指标组合中&#xff0c;不能用于系统性评价缺陷识别模型精度的指标为&#xff1a; A 检出率和准确率 B 检出率和误报比 C 平均精确率 D 准确率和误报比 对于评价一个缺陷识别模型的精度&#xff0c;检出率、准确率、检出率和误报比等指标常被用来系统性地评估模型的效…

Web安全 - “Referrer Policy“ Security 头值不安全

文章目录 概述原因分析风险说明Referrer-Policy 头配置选项1. 不安全的策略no-referrer-when-downgradeunsafe-url 2. 安全的策略no-referreroriginorigin-when-cross-originsame-originstrict-originstrict-origin-when-cross-origin 推荐配置Nginx 配置示例 在 Nginx 中配置 …

spring mvc源码学习笔记之六

pom.xml 内容如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…

Hyperbolic dynamics

http://www.scholarpedia.org/article/Hyperbolic_dynamics#:~:textAmong%20smooth%20dynamical%20systems%2C%20hyperbolic%20dynamics%20is%20characterized,semilocal%20or%20even%20global%20information%20about%20the%20dynamics. 什么是双曲动力系统&#xff1f; A hy…

vue3组件化开发优势劣势分析,及一个案例

Vue 3 组件化开发的优势和劣势 优势 可复用性&#xff1a; 组件可以重复使用&#xff0c;减少代码冗余&#xff0c;提高开发效率。 可以在不同的项目中复用组件&#xff0c;提升开发速度。 可维护性&#xff1a; 组件化开发使得代码结构清晰&#xff0c;易于维护。 每个…