当前位置: 首页 > news >正文

860. 柠檬水找零

目录

题目链接:

题目:

解题思路:

代码:

总结:


题目链接:

860. 柠檬水找零 - 力扣(LeetCode)

题目:

解题思路:

设置两个int型变量记录5元和10元的数量,遇到5元直接a++,遇到10元a--,b++;

遇到20:先使用10元的和5元的结合,再使用3张5元;这个过程中10元和20元的都得判断,若不行直接返回false;最终返回true即可

代码:

class Solution {public boolean lemonadeChange(int[] bills) {int a=0,b=0;for(int i=0;i<bills.length;i++){if(bills[i]==5){a++;}else if(bills[i]==10){if(a>=1){a--;b++;}else return false;}else{if(b>=1&&a>=1){a-=1;b--;}else if(a>=3){a-=3;}else return false;}}return true;}
}

柠檬水找零:一次优雅的贪心算法实践
在算法的世界里,有些问题看似简单,却蕴含着深刻的策略思想。“柠檬水找零”(Lemonade Change)就是这样一个绝佳的例子。它不仅是一个经典的贪心算法应用场景,而且其解法思路可以广泛地应用于类似的 “资源分配” 问题中。
我将带你从问题本身出发,分析找零的核心逻辑,探索可能的解法,最终理解并掌握这个只需一次遍历的优雅贪心方案。
一、问题重述:一杯柠檬水引发的思考
首先,让我们清晰地理解问题:
场景:你正在卖柠檬水,一杯售价 5 美元。
支付方式:顾客只能用 5 美元、10 美元 或 20 美元 的纸币来付款。
你的初始状态:你一开始没有任何零钱。
目标:对于每一位前来购买的顾客,你都必须能准确地找零。你需要判断,能否成功地为所有顾客找零。
约束:
你只能用你手头已有的零钱进行找零。
顾客是按顺序前来的,你必须当场解决当前顾客的找零问题。
这个问题的核心在于,你如何管理你手中的零钱,以应对不同面额的付款。
二、初步思考与暴力解法的困境
面对这个问题,最直接的想法是什么?
暴力解法(不可行):我们可以尝试记录下手中所有零钱的具体情况(比如,有几张 5 元,几张 10 元,几张 20 元)。当收到一张新的钞票时,我们尝试所有可能的找零组合,看是否能凑出需要的金额。如果能,就更新手中的零钱状态,继续处理下一位顾客。
为什么不可行?
状态空间巨大:如果顾客很多,你手中的钞票数量会增加,记录所有钞票的状态是不现实的。
效率低下:对于每一笔交易,你都需要探索所有可能的找零组合,这会导致算法的时间复杂度急剧上升。
暴力解法显然不是我们想要的。我们需要一个更高效、更具策略性的方法。
三、核心洞察:贪心策略的选择
让我们来分析一下找零的逻辑,寻找其中的规律。我们有三种付款情况:
顾客支付 5 美元:
找零需求:$5 - $5 = $0。不需要找零。
操作:直接将这张 5 美元收入囊中。这会增加我们未来找零的灵活性。
顾客支付 10 美元:
找零需求:$10 - $5 = $5。需要找给顾客 一张 5 美元。
操作:检查我们手中是否有 5 美元的零钱。
如果有,就找给顾客,同时我们的 5 美元数量减一,10 美元数量加一。
如果没有,交易失败,直接返回 false。
这是唯一的选择,没有其他组合可以凑出 5 美元。
顾客支付 20 美元:
找零需求:$20 - $5 = $15。需要找给顾客 15 美元。
关键决策点:凑出 15 美元有两种方式:
方案 A:一张 10 美元 + 一张 5 美元 (10 + 5 = 15)
方案 B:三张 5 美元 (5 + 5 + 5 = 15)
现在,问题来了:当两种方案都可行时,我们应该选择哪一种?
这就是贪心算法的用武之地。贪心的本质是在每一步都做出当前看起来最优的选择,而不考虑这一步选择对未来的影响,期望由局部最优解导出全局最优解。
那么,对于找零 15 美元,哪个方案是 “局部最优” 的?
5 美元钞票是 “万能” 的:它可以用来找零 5 美元(给 10 元顾客)和 15 美元(给 20 元顾客)。
10 美元钞票的用途有限:它只能用来找零 15 美元(给 20 元顾客)。
如果我们选择方案 B(用三张 5 美元),会过早地消耗掉我们手中 “万能” 的 5 美元。这可能导致后续来了一个支付 10 美元的顾客时,我们没有 5 美元可以找零,从而导致整个交易链失败。
而如果我们选择方案 A(用一张 10 美元和一张 5 美元),我们消耗了一张用途有限的 10 美元,同时只消耗了一张 5 美元。这样可以保留更多的 5 美元,以应对未来更多的找零需求。
结论(贪心选择):当需要找零 15 美元时,应优先使用 “一张 10 美元 + 一张 5 美元” 的组合。只有在没有 10 美元时,才考虑使用 “三张 5 美元” 的组合。
这个选择策略,是解决整个问题的关键。
四、算法设计:一次遍历的优雅实现
基于以上的贪心策略,我们可以设计出一个非常高效的算法:
步骤 1:状态表示我们不需要记录所有钞票,只需要用两个变量来跟踪我们手中关键的零钱数量:
fiveCount:记录手中 5 美元钞票的数量。
tenCount:记录手中 10 美元钞票的数量。
为什么不记录 20 美元? 因为 20 美元的钞票在这个问题中无法用于找零,所以我们不需要关心它的数量。
步骤 2:遍历与决策遍历 bills 数组,对于每一张收到的钞票 bill:
如果 bill == 5:
无需找零。
fiveCount++。
如果 bill == 10:
需要找零 5 美元。
检查 fiveCount 是否大于 0。
如果是,fiveCount--,tenCount++。
如果否,返回 false。
如果 bill == 20:
需要找零 15 美元。
优先执行贪心策略:
检查是否同时拥有 10 美元和 5 美元 (tenCount > 0 && fiveCount > 0)。
如果是,tenCount--,fiveCount--。
如果不满足上述条件,再检查是否拥有至少三张 5 美元 (fiveCount >= 3)。
如果是,fiveCount -= 3。
如果以上条件都不满足,则无法找零,返回 false。
步骤 3:完成遍历如果成功处理完所有顾客的付款,说明我们为每一位顾客都成功找零了。返回 true。
五、代码解析:一行一行读懂它
现在,让我们来解析你提供的这段代码。它完美地实现了上述的贪心算法。
java
运行
class Solution {
    public boolean lemonadeChange(int[] bills) {
        // 1. 初始化状态变量
        // a 用来记录 5 美元钞票的数量
        // b 用来记录 10 美元钞票的数量
        int a = 0, b = 0;

        // 2. 遍历每一位顾客的付款
        for (int i = 0; i < bills.length; i++) {
            // 情况一:顾客支付 5 美元
            if (bills[i] == 5) {
                a++; // 无需找零,直接将 5 美元收入囊中
            } 
            // 情况二:顾客支付 10 美元
            else if (bills[i] == 10) {
                // 检查是否有 5 美元可以找零
                if (a >= 1) {
                    a--; // 给出一张 5 美元
                    b++; // 收入一张 10 美元
                } else {
                    // 如果没有 5 美元,无法找零,直接返回 false
                    return false;
                }
            } 
            // 情况三:顾客支付 20 美元
            else { // bills[i] == 20
                // 贪心策略:优先使用 10 + 5 的组合来找零 15 美元
                if (b >= 1 && a >= 1) {
                    a -= 1; // 给出一张 5 美元
                    b -= 1; // 给出一张 10 美元
                } 
                // 如果无法使用 10 + 5 的组合,再尝试使用 5 + 5 + 5 的组合
                else if (a >= 3) {
                    a -= 3; // 给出三张 5 美元
                } 
                // 如果两种组合都无法满足,说明无法找零
                else {
                    return false;
                }
            }
        }

        // 3. 如果成功处理完所有顾客,说明可以为所有人找零
        return true;
    }
}
代码中的变量名说明:
你提供的代码中使用了 a 和 b 作为变量名,这在算法竞赛中很常见,追求的是简洁。在实际开发中,为了代码的可读性,更推荐使用 fiveCount 和 tenCount 这样的命名。两者在逻辑上是完全等价的。
代码执行流程模拟:让我们用一个例子来走一遍流程:bills = [5, 5, 5, 10, 20]
初始化: a = 0, b = 0
i = 0, bill = 5:
a++。a 变为 1。状态: {5:1, 10:0}
i = 1, bill = 5:
a++。a 变为 2。状态: {5:2, 10:0}
i = 2, bill = 5:
a++。a 变为 3。状态: {5:3, 10:0}
i = 3, bill = 10:
a >= 1 为 true。
a-- (a 变为 2), b++ (b 变为 1)。状态: {5:2, 10:1}
i = 4, bill = 20:
b >= 1 && a >= 1 为 true (b=1, a=2)。
a-- (a 变为 1), b-- (b 变为 0)。状态: {5:1, 10:0}
遍历结束。
函数返回 true。我们成功为所有顾客找零。
另一个失败的例子:bills = [5, 10, 10]
i=0, bill=5: a 变为 1。
i=1, bill=10: a 变为 0, b 变为 1。
i=2, bill=10: a >= 1 为 false。直接返回 false。
六、复杂度分析与总结
复杂度分析
时间复杂度:O(N)。我们只需要对 bills 数组进行一次遍历,N 是顾客的数量。
空间复杂度:O(1)。我们只使用了 a 和 b 两个额外变量来存储状态,与输入数组的大小无关。
总结
这个 “柠檬水找零” 问题是贪心算法的一个绝佳范例。它的优雅之处在于:
策略的明确性:问题的核心决策点非常清晰 —— 如何处理 20 美元的付款。
贪心选择的正确性:通过分析不同面额钞票的 “通用性”,我们可以明确地判断出 “优先使用 10+5” 的策略是局部最优的。
实现的高效性:基于这个明确的贪心策略,我们可以设计出时间复杂度为 O(N)、空间复杂度为 O(1) 的最优解法。
这个问题告诉我们,当遇到需要在多个可行选项中进行选择的场景时,不妨停下来思考一下:哪个选择能为未来保留更多的灵活性?哪个选择的 “通用性” 更强? 遵循这个原则,往往能找到问题的贪心解法。
希望这篇文章能帮助你彻底理解这个问题,并从中体会到贪心算法的魅力!

总结:

这是一道关于找零问题的算法题解。解题思路是用两个变量分别记录5元和10元的数量,根据顾客支付的金额(5/10/20元)进行相应处理:5元直接收下,10元需找5元,20元优先用10+5组合找零,其次用3张5元。如果找不开就直接返回false。代码实现简洁高效,时间复杂度O(n),空间复杂度O(1)。该解法通过贪心策略正确模拟了找零过程,是典型的贪心算法应用场景。

http://www.dtcms.com/a/412099.html

相关文章:

  • 协会网站建设模板开发者账号注册流程
  • 企业seo网站推广小程序制作网站
  • 因果推断中的d-分离图模型
  • Markdown 完整使用指南
  • 深度解析Docusaurus:现代化静态站点生成器的技术架构与创新实践
  • rgb565和rgb565A8的区别
  • 2023免费网站推广wordpress模板最新
  • 视频网站文案开网站需要什么流程
  • 单仁资讯做网站怎样网站商城与网站区别吗
  • 农林网站建设西安有关做网站的公司
  • 基于PLC的粉料输送系统(论文+源码)
  • IP代理在数据采集中的重要性
  • 网站制作理念暴雪战网
  • 【入门级-算法-6、排序算法: 计数排序】
  • 传奇网站架设方法做网站赔钱了
  • 智慧停车大屏改造详细修改文档
  • 做网站需要icp吗园林景观设计平面图
  • Visual Studio 插件 - 喝水提醒
  • 景区网站建设 现状中俄跨境电商平台有哪些
  • 福建省教师空间建设网站稿定设计网页版登录
  • 摄影网站都有什么网站应用系统设计方案
  • 2014-2024高考真题考点分布详细分析(另附完整高考真题下载)
  • 天津站内关键词优化手机搜索引擎
  • asp网站源码东莞商城网站建设公司
  • 京东商城网站地址是多少深圳龙华区大浪街道
  • 百度竞价推广托管长春seo排名公司
  • 论文《A Recurrent Latent Variable Model for Sequential Data》翻译
  • 笔记(C++篇)—— Day 12
  • 建立网站准备工作凡科网站源码下载
  • 将数组最后一位元素移动到数组首元素位置,其余元素依次向后移动一位