DAY38:贪心算法(六)分发糖果+柠檬水找零

news2025/4/27 17:01:32

文章目录

    • 135.分发糖果
      • 思路
      • 第一种情况:右>左
      • 第二种情况:左>右(倒序遍历)
        • 两种情况的结果合并,通过取最大值
      • 完整版
      • 总结
    • 860.柠檬水找零
      • 思路
      • 完整版
      • 总结

135.分发糖果

  • 本题涉及到一个思想,就是处理好一边再处理另一边,不要两边想着一起兼顾。也就是说递增递减都要处理的情况,需要遍历两遍。后面还会有题目用到这个思路。

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 212 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 121 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

提示:

  • n == ratings.length
  • 1 <= n <= 2 * 10^4
  • 0 <= ratings[i] <= 2 * 10^4

思路

本题大致思路如下图所示。注意,因为只给出了两个条件,没有说数值相等的情况,因此数值相等,又没有比左右评分高的情况,只需要基础的1就可以了。

在这里插入图片描述
本题,因为是相邻的孩子作比较,我们需要同时考虑两边的情况,也就是考虑左边的小孩和右边的小孩。

这种需要同时考虑两边的情况,例如同时需要比较左边小孩+右边小孩,一定要先确定一边再确定另一边!也就是说需要把一边的情况全部确认完毕,再去确认另一边。两边一起比较一定会乱

每个小孩既要和左边比较,又要和右边比较,此时首先确定一边,也就是右边小孩>左边小孩的情况。左边小孩>右边的情况,我们先不管。

第一种情况:右>左

右孩子>左孩子的情况:

这种情况,首先考虑极端情况,也就是一路都是右边>左边。这样的话,糖果的数量是从左到右累积,因此遍历也需要从左往右遍历

在这里插入图片描述

//判断逻辑
if(rating[i]<rating[i+1]){
    candy[i+1]=candy[i]+1;
}

第二种情况:左>右(倒序遍历)

左孩子>右孩子的情况如图所示。注意,左>右这种情况需要从后往前遍历

原因是,由于本题相邻孩子评分更高会获得更多糖果,因此我们需要考虑极端情况,也就是一路上都是左>右的情况

如果一路上都是左>右的情况那么糖果的数目需要从后向前累积!也就是说,我们必须采用倒序遍历,才能接收到左孩子>右孩子的累积结果

在这里插入图片描述

两种情况的结果合并,通过取最大值

第二种情况还有一个需要注意的问题,就是到了第二种情况的时候,candy[i]已经有第一种情况的数值了。此时,为了满足既要比较左边,又要比较右边,需要对两个方法得到的candy[i]取最大值。取最值的逻辑如下图:

绿色字体是第二个例子中,以右>左规则来遍历的结果。由此可见需要的操作是取max值。

在这里插入图片描述

//判断逻辑
for(int j=ratings.size()-1;j>0;j--){
    if(ratings[j]<ratings[j-1]){
        //此时candy[j]本身就有数值了,取一个最大值,从而同时满足两边的条件
        candy[j-1] = max(candy[j]+1,candy[j-1]);
    }
}

完整版

  • 本题在左大于右的时候,必须采用倒序遍历,就是考虑了极端情况下,如果所有的孩子都是左大于右,评分数组降序排列,那么此时左边的孩子是累积了这个数组里所有的糖果情况!此时如果还是正序,那么将会导致左孩子没有累积到右孩子+1的结果(因为左孩子更大)
  • 本题在左右两次遍历结果合并的时候,是取两次遍历结果里面的最大值。做一个单调递增/递减的例子就可以看出。
class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int>candy(ratings.size(),1);
        int sum=0;
        //先考虑右孩子大于左孩子
        for(int i=0;i<ratings.size()-1;i++){
            if(ratings[i]<ratings[i+1]){
                candy[i+1]=candy[i]+1;
            }
        }
        //再考虑左孩子大于右孩子
        for(int j=ratings.size()-1;j>=1;j--){
            if(ratings[j]<ratings[j-1]){
                //注意这里比较大的是candy[j-1],需要和candy[j-1]本身数值进行取最值
                candy[j-1]=max(candy[j]+1,candy[j-1]);
            }
        }
        for(int i:candy){
            sum+=i;
        }
        return sum;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

总结

这在leetcode上是一道困难的题目,其难点就在于贪心的策略,如果在考虑局部的时候想两边兼顾,就一定会顾此失彼。评分数组既包含递增又包含递减的部分,必须经过两次遍历,一次遍历不能同时兼顾。

本题采用了两次贪心的策略

  • 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。

这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。另外,本题要注意极端情况考虑的思想,我们想到倒序遍历这个想法,主要就是考虑评分数组单调递减/单调递增的极端情况

860.柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false

示例 1:

输入:bills = [5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 35 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true

示例 2:

输入:bills = [5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 25 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false

提示:

  • 1 <= bills.length <= 10^5
  • bills[i] 不是 5 就是 10 或是 20

思路

本题一开始手里是没有零钱的,只能靠收到顾客的钱。最开始感觉很复杂,但是本题的场景非常固定

场景包括:

1.顾客支付了5,不用找零

2.顾客支付了10,能找零返回5,不能的话返回false

3.顾客支付了20,要么返回10和5,要么返回5和5和5,否则返回false

注意第三种情况,我们需要优先采用10和5这种策略,因为5更万能一些5不仅可以对20找零,也可以对10找零,所以手头上要尽量留更多的5.

贪心的策略就是,遇到20的情况,优先用10进行找零。全局最优就是完成所有找零。

其他的策略都是固定的,场景也是固定的,直接写死即可。

完整版

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        //先定义三个变量,统计5,10,20的数量
        int five=0;
        int ten=0;
        int twenty=0;
        for(int i=0;i<bills.size();i++){
            if(bills[i]==5){
                five++;
                continue;
            }
            if(bills[i]==10){
                if(five==0) return false;
                else{
                    five--;
                    ten++;
                    continue;
                }
            }
            if(bills[i]=20){
                //优先用10+5的策略来找零
                if(ten>0&&five>0){
                    ten--;
                    five--;
                    twenty++;
                    continue;
                }
                //然后采用5+5+5的策略
                else if(five>=3){
                    five-=3;
                    twenty++;
                    continue;
                }
                else
                    return false;
            } 
        }
        //如果for结束了还没有return false,就说明可以遍历完所有顾客
        return true;

    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

总结

这道题目看起来比较复杂,但是实际上策略非常固定,因为顾客只有5 10 20三种情况,而且我们手里也没有其他面额的零钱,只能用5 10 20来进行找零。因此可以用if语句列出所有的情况,再配合贪心的策略,优先用10找零,留下5。

这道题目可以看出,遇到感觉没有思路的题目,可以静下心来把能遇到的所有情况分析一下,只要分析到具体情况了,一下子就豁然开朗了。

如果一直陷入想从整体上寻找找零方案,就会把自己陷进去,各种情况一交叉,只会越想越复杂。

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

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

相关文章

位图的详解

目录 位图 位图的概念 位图的实现 位图常见三道面试题 1.给定100亿个整数&#xff0c;设计算法找到只出现一次的整数&#xff1f; 2. 给两个文件&#xff0c;分别有100亿个整数&#xff0c;我们只有1G内存&#xff0c;如何找到两个文件交集&#xff1f; 3. 位图应用变形…

C#程序以管理员权限运行

在Vista 和 Windows 7 及更新版本的操作系统&#xff0c;增加了 UAC(用户账户控制) 的安全机制&#xff0c;如果 UAC 被打开&#xff0c;用户即使以管理员权限登录&#xff0c;其应用程序默认情况下也无法对系统目录、系统注册表等可能影响系统正常运行的设置进行写操作。这个机…

【sap2000】【python】python相关的3个案例-1/3

python相关的3个案例 Python COM&#xff0c;Python NET&#xff0c;IronPython的区别 这三个术语都与 Python 语言和其他编程平台&#xff08;尤其是 Microsoft .NET 及其组件&#xff09;之间的互操作性有关。我们来看看它们之间的主要区别&#xff1a; Python COM&#xf…

【vim】Linux使用vim编写代码:头部自动添加提示信息+自动缩进、自动换行等配置(~/.vimrc)

前言&#xff1a; 在编写代码时&#xff0c;为了提高代码的可读性和维护性&#xff0c;我们经常在文件的头部添加一些信息提示&#xff0c;如作者、日期、版本号等。本文介绍了如何在 Vim 编辑器中实现自动添加信息提示的功能。 结尾提供~/.vimr参考配置&#xff0c;可提高代码…

第2章 变量

目录 1 变量1.1 什么是变量&#xff1f;1.2 声明变量1.2 变量的原理 2 数据类型2.1 基本数据类型2.1.1 取值范围公式2.1.2 整型2.1.3 浮点型2.1.4 字符型2.1.5 布尔型 2.2 引用数据类型2.2.1 数组[]2.2.2 类class2.2.3 接口interface 3 数据类型转换3.1 自动类型转换3.2 强制类…

备份c盘中的所有数据的3种方法推荐!

为什么要备份C盘中的所有数据&#xff1f; 备份c盘中的所有数据&#xff0c;主要是为了避免以下几种突发情况&#xff1a; 感染病毒。 操作不当导致系统文件损坏&#xff0c;崩溃。 蓝屏死机&#xff08;BOSD&#xff09;或黑屏死机。 物理灾害。 磁盘出现物理坏道等。…

QWebEngine应用---cookies存储及自动登录

什么是cookies&#xff1f; 浏览器Cookie指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据&#xff0c;当前主流网站和浏览器都使用Cookies来实现自动登录 QWebEngineCookieStore QWebEngine基于Chromium内核&#xff0c;和Chrome浏览器一样支持…

微信聚合聊天,轻松一人管理多个微信

看这篇文章的你是否有以下烦恼&#xff1a; 1.微信账号太多&#xff0c;管理过于麻烦 2.微信号多&#xff0c;需要很多员工来管理&#xff0c;人工费用多 3.多个微信打开后会造成微信登陆界面过多&#xff0c;切换操作十分不方便 4.当微信多的时候&#xff0c;新消息提示也多…

docker安装nacos并配置数据库

1、下载镜像 docker pull nacos/nacos-server 查看下载镜像 docker images 2、启动nacos 注意&#xff1a;如果nacos版本在2.0及以上&#xff0c;需要把8848、9848和9849三个端口映射出来&#xff0c;否则访问会404失败 docker run --env MODEstandalone \ --name nacos -d…

【AI新趋势期刊#2】AI发明计算机算法,如何给大模型排行,照片秒变二维码,视频一键动漫风

前言 每天都要浏览大量AI相关新闻&#xff0c;是不是感到信息量爆炸&#xff0c;有效信息少&#xff1f; 这么多新产品和新工具&#xff0c;到底哪些是真正是有价值的&#xff0c;哪些只是浮躁的一时热点&#xff1f; 想参与AI产品和工具的开发&#xff0c;从哪里能够获得大…

MongoDB【MongoDB命令、CRUD操作 】(二)-全面详解(学习总结---从入门到深化)

目录 MongoDB命令 CRUD操作 MongoDB命令 基本操作 查看数据库 show dbs; 切换数据库 如果没有对应的数据库则创建 use 数据库名;创建集合 db.createCollection("集合名") 查看集合 show tables; show collections; 删除集合 db.集合名.drop(); 删除当前…

Robust Secure Aggregation with Lightweight Verification for Federated Learning

摘要&#xff1a; 可验证的安全聚合&#xff08;VSA&#xff09;是联邦学习&#xff08;FL&#xff09;中的关键过程&#xff0c;其中安全聚合实现局部梯度聚合&#xff0c;同时保留数据机密性&#xff0c;并且可验证性使参与者能够验证中央服务器&#xff08;CS&#xff09;返…

【Linux】程序替换

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️博客专栏&#xff1a;✈️Linux之路       &#x1f6f0;️社区 :✈️ 进步学堂       &a…

51单片机地震监测语音报警提示系统MPU6050TTS报警

实践制作DIY- GC0153--- 地震监测语音报警提示系统 基于51单片机设计------- 地震监测语音报警提示系统 二、功能介绍&#xff1a; STC89C52单片机TTS语音播报模块MPU-6050角度传感器LED灯显示一个按键 1.获取MPU-6050角度数据&#xff0c;然后根据角度数据计算出0~10级的地震…

B站w_rid,qv_id加密分析

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01; 网站地址&#xff1a;aHR0cHM6Ly9zZWFyY2guYmlsaWJpbGkuY29tL2FsbD92dD03OTExMjUwNSZrZXl3b3JkPSVFNyU4OCVBQyVFOCU5OSVBQg 关键字搜…

Flutter 布局构建

文章目录 一、布局类组件简介二、理解 Flutter 布局约束三、线性布局&#xff08;Row和Column&#xff09;1. 主轴和纵轴2. Row3. Column4. 特殊情况 四、弹性布局&#xff08;Flex 和 Expanded&#xff09;1. Flex2. Expanded 五、流式布局&#xff08;Wrap 和 Flow&#xff0…

记录一次Ubuntu系统安装tenforflow

电脑已有版本 Ubuntu 20.04GCC 9.4.0CUDA 11.3 需要指定版本的python库 tensorflow 2.6.0numpy 1.19.2matplotlib 3.4.0keras 2.6.0protobuf 3.19.6 我的环境

LeetCode刷题 | 583. 两个字符串的删除操作、72. 编辑距离

583. 两个字符串的删除操作 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 示例 1&#xff1a; 输入: word1 "sea", word2 "eat" 输出: 2 解释: 第一步将 "…

Flutter Bloc组件buildWhen的妙用

在Flutter中当状态发生改变的时候,Widget会重新build刷新页面。但是当状态发生改变的时候后,我们指向让有关联的Widget重绘,与之无关的Widget保持不变,比如对于登录页面,有用户名和密码两个组件:如下图。 构建代码如下: 当我们输入用户名的时候,仅仅希望_UserNameInp…

C语言王国探险记之转义字符+结构语句

王国探险记系列 文章目录&#xff08;4&#xff09; 一&#xff0c;什么是转义字符 1.1转义字符的重要性&#xff1f; 二&#xff0c;了解一下转义字符有那些 三&#xff0c;精讲转义字符 3.1转义字符\n 3.2转义字符\? 3.3转义字符 \‘和\" 3.3转义字符 \\ 3.4转义字…