Studying-代码随想录训练营day27| 贪心算法理论基础、455.分发饼干、376.摆动序列、53.最大子序和

第27天,贪心开始!(ง •_•)ง💪,编程语言:C++

目录

贪心算法理论基础

贪心的套路

贪心的一般解题步骤

总结 

455.分发饼干

376.摆动序列

53.最大子序和

总结 


贪心算法理论基础

什么是贪心?—— 贪心的本质是选择每一个阶段的局部最优,从而达到全局最优。例如:你可以拿走十张,如果想达到最大的金额,你要怎么拿?指定每次拿最大的,最终结果就是拿走最大数额的钱!

但要注意的是只有局部最优能够推出全局最优,也就是没有反例的情况下,才能够使用贪心算法。因为比如说再举一个例子如果是 有一堆盒子,你有一个背包体积为n,如何把背包尽可能装满,如果还每次选最大的盒子,就不行了,需要考虑了的条件增加了许多,这时候就需要动态规划,而不是贪心了。

贪心的套路

总结:贪心算法没有固定套路。说到底贪心只有一句话通过局部最优,推出整体最优,而能否从局部最优推出整体最优,只能通过思考,模拟,并且举反例的方式来确定。一般来说最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧

对于贪心来说最重要的是能够想到模拟策略,面试中最重要的也是能够自圆其说即可。刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心

贪心算法很多情况下都是常识性推导,让人认为本应该就这么做,这也是贪心难的地方,能够想到就能解出,但如果想不到就很难解出了。

贪心的一般解题步骤

贪心算法一般分为四步:

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

但事实上以上四步有些过于理想化,我们做题的时候不会说按照这样去思考,因此最重要的还是想清楚局部最优是什么,并且如何推导出全局最优。

总结 

贪心算法没有套路,有的只是常识性的推导和灵感的碰撞。


455.分发饼干

文档讲解:代码随想录分发饼干

视频讲解:手撕分发饼干

题目:

学习:本题算是比较经典的贪心算法之一。首先需要理解题干的意思:有一堆饼干和一群小孩,把饼干分给小孩,但每个小孩只能分得一块饼干。每块饼干具有一个尺寸值,每位小孩具有一个胃口值,只有当尺寸值大于胃口值时,小孩才能够满足。

给予以上分析,可以推出局部最优就是尽可能大尺寸的饼干满足大胃口的人(牢记一块饼干只能给一个人),从而得到总体最优满足最多的人。

根据上述策略,我们首先要对饼干和小孩两个数组进行排序,而后选择尺寸最大的饼干,之后遍历小孩找到符合要求的最大胃口的小孩,接着在选择下一块饼干继续遍历(要注意遍历小孩我们也是从胃口最大的开始遍历,因此选择下一个饼干时我们不需要从头遍历,因为胃口大的那些小孩肯定不会满足尺寸更小的饼干,继续遍历就可以了)

代码:

//时间复杂度O(nlogn + mlogm)两个排序的时间复杂度
//空间复杂度O(1)
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        int count = 0; //记录小孩数量
        //先进行排序
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int index = s.size() - 1; //指向最后一个元素,此时为最大饼干
        for(int i = g.size() - 1; i >= 0 && index >= 0; i--) {
            if(g[i] <= s[index]) {
                count++;
                index--;
            }
        }
        return count;
    }
};

本题还可以采取小饼干先喂小胃口的人的方法,但此时要注意先选择小胃口的人,然后遍历饼干,因为此时我们需要的是找到满足最小胃口的最小饼干,和前面的找到满足最大饼干的最大胃口是相反的。

代码:

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        //第二种方法,先遍历胃口再遍历饼干(先满足小胃口的)
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int index = 0;
        //遍历饼干,找到符合最小胃口的小孩
        for(int i = 0; i < s.size() && index < g.size(); i++) {
            if(s[i] >= g[index]) {
                index++; //查找下一个胃口小的小孩
            }
        }
        return index; //满足了多少个小孩,就是答案数量
    }
};

376.摆动序列

文档讲解:代码随想录摆动序列

视频讲解:手撕摆动序列

题目:

学习:本题理解题意,摆动序列指的是数字之间的差是正负数摇摆交替的,可以通过删除一些元素,来使得剩余元素组成的序列是摇摆序列。如果仅有一个元素或者含两个不等元素的序列也视作摇摆序列。

首先我们很容易能够想到,找到波峰和波谷,删除中间多余的元素,就能够组成一个摇摆序列。

在解题过程中,我们不需要删除多余的元素,只需要保存波峰和波谷的元素就能够得到元素最多的摆动序列,并且本题甚至只需要返回长度值,不需要我们把元素进行保存。

但本题不能仅仅判断左右差值是否为正负,我们需要考虑除上图情况以外的三种情况:

情况一:上下坡中有平坡

对于这种情况,显然是把中间多余的三个3去除,最后留下(1,2,1)序列,也就是长度为3。但本题2在的位置不是明显的波峰或是波谷,不能通过判断左右差值为正负来进行。因此我们需要单独考虑该情况的逻辑。

当i指向第一个2的时候,prediff > 0 & curdiff = 0,当i指向最后一个2的时候,prediff = 0 && curdiff < 0。这两个条件都可以,但是为了便于我们进行从左到右的遍历,我们这边采用删除左面三个2的规则,因此当prediff = 0 && curdiff < 0时要记录一个峰值,这是上坡的情况。下坡的情况就是prediff = 0 && curdiff > 0的情况也要记录一个峰值。加上前面的判断正负的情况,因此我们要记录峰值的条件应该是:(prediff <= 0 && curdiff > 0) || (prediff >= 0 && curdiff < 0)。

情况二:数组首尾两端

因为本题的要求,如果只有一个元素则摆动序列为1,如果有两个元素且两个元素不同的情况下摆动序列为2。但我们在计算prediff和curdiff的时候,至少需要三个数字才能进行计算。我们可以通过单独列出一个if 来处理当元素只有两个的情况。但我们也可以将其加入到统一的循环当中,那就是让prediff初始 = 0。例如我们针对序列[2,5],可以假设为[2,2,5],这样也就有了坡度。

针对上述情形,我们的result初始为1,默认最右面有一个数值(符合我们上面说的遍历规则)。当出现上述情况的时候,result就会+1,最后得到的就是2。这样也能够处理超过两个数并且所有数都是一样的情况。

情况三:单调坡度有平坡 

可以看出针对这种情况,我们在情况一中使用的方法可能就会带来错误,因为上述的序列,我们最后得到的只能是[1,4]或者时其他长度为2的摆动序列。因此我们这边要注意的是prediff的更新逻辑。prediff不能实时进行更新,它保存的不仅仅是差值还应是数组的一个趋势,因此prediff应该在有坡度摆动变化的时候进行更新,也就是出现峰值的时候我们再进行更新。

代码:解决上述三种情况后,可以得到代码:

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.size() == 1) return 1; //如果只有一个元素,返回1
        int prediff = 0; //前端差值
        int curdiff = 0; //后段差值
        int result = 1; //记录结果,默认加入尾元素,首元素单独处理,防止出现所有元素都相同的情况
        //不遍历尾元素
        for (int i = 0; i < nums.size() - 1; i++) {
            curdiff = nums[i + 1] - nums[i];
            //出现峰值
            if((prediff >= 0 && curdiff < 0) || (prediff <= 0 && curdiff > 0)) {
                result++;
                prediff = curdiff; //当需要记录结果的时候,再改变前端差值,保证当前数组趋势
            }
        }
        return result;
    }
};

53.最大子序和

文档讲解:代码随想录最大子序和

视频讲解:手撕最大子序和

题目:

学习:本题显然可以通过两个for循环进行暴力求解。但本题同样也可以使用贪心算法来降低时间复杂度。本题的贪心逻辑在于我们从头开始遍历的过程中,如果发现加和为负数,就立马抛弃目前的“连续和”,因为负数参与加和只会使得数变小,而不会变大。因此我们需要立马抛弃该和,从下一个元素重新计算“连续和”,由这种局部最优的方式来推导出全局最优。

注意:我们是在加和为负数的情况下,才去抛弃“连续和”,而不是遇到负数就抛弃,两者是有区别的,因为加和还不为负数时,仍然是可以给后面的数提供加大的可能性的。但我们也要通过设置一个maxcount实时记录当前count的最大值,这样也能够避免我们因为存在负数而错过最大值的情况。

因为本题不需要我们记录最大序列的左右下标,因此本题设置一个result或者maxcount记录最大值就可以了,否则还需要设置两个左右端点来记录下标。

代码:

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int count = 0; //记录当前和
        int maxcount = INT_MIN; //记录最大值
        for(int i = 0; i < nums.size(); i++) {
            //如果当前和小于0,抛弃前和,改为当前值
            if(count < 0) {
                count = nums[i];
            }
            else {
                count += nums[i];
            }
            //如果当前和大于最大值,更新最大值
            if (count > maxcount) {
                maxcount = count;
            }
        }
        return maxcount;
    }
};

总结 

贪心算法更重要的是考察逻辑思维能力,找到局部最优推出总体最优的方法,要尝试进行模拟,并推出反例,如果推不出反例就大胆尝试使用贪心方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自动化设备上位机设计 三

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using SqlSugar;namespace 自动化上位机设计 {public partial class Form1 : Form{SqlHelper sqlHelper new SqlHelper();SqlSugarClient dbContent null;bool IsRun false;int Count 0;public Form1(){Initializ…

SpringBoot新手快速入门系列教程五:基于JPA的一个Mysql简单读写例子

现在我们来做一个简单的读写Mysql的项目 1&#xff0c;先新建一个项目&#xff0c;我们叫它“HelloJPA”并且添加依赖 2&#xff0c;引入以下依赖&#xff1a; Spring Boot DevTools (可选&#xff0c;但推荐&#xff0c;用于开发时热部署)Lombok&#xff08;可选&#xff0c…

Google Earth Engine(GEE)——在控制台打印出来所选区域的缩略图

结果 函数 ui.Thumbnail(image, params, onClick, style) A fixed-size thumbnail image generated asynchronously from an ee.Image. Arguments: image (Image, optional): The ee.Image from which to generate the thumbnail. Defaults to an empty ee.Image. param…

简单分享下python多态

目录&#xff1a; 一、多态是啥嘞&#xff08;龙生九子各有不同&#xff0c;这就是多态&#xff09; 二、基础的实例 三、多态的优势与应用场景 四、深入理解 一、多态是啥嘞&#xff08;龙生九子各有不同&#xff0c;这就是多态&#xff09; 多态&#xff08;Polymorphism&…

Laravel5+mycat 报错 “Packets out of order”

背景 近期对负责项目&#xff0c;配置了一套 主从复制的 MySQL 集群 使用了中间件 mycat 但测试发现&#xff0c;替换了原来的数据连接后&#xff0c;会出现 Packets out of order 的报错 同时注意到&#xff0c;有的框架代码中竟然也会失效&#xff0c;比如 controller 类中&…

Mac电脑iTerm2 如何设置无限滑动

1.打开iTerm2应用 2.打开偏好设置 3.选中Profiles -> Terminal 4.选择Unlimited scrollback

Linux开发讲课33---线程实现与线程控制步骤简析

线程概述 进程是系统中程序执行和资源分配的基本单位。 每个进程都拥有自己的数据段、代码段和堆栈段&#xff0c;这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销&#xff0c;进程在演…

Ollama+OpenWeb UI搭建最简单的大模型交互界面

Open WebUI是一个专为大型语言模型&#xff08;LLMs&#xff09;设计的Web用户界面。这个界面提供了一个直观、响应迅速且易于使用的平台&#xff0c;使用户能够与本地运行的语言模型进行交互&#xff0c;就像与云服务中的模型交互一样。可以非常方便的调试、调用本地模型。你能…

Interpretability 与 Explainability 机器学习

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识 Interpretability 模型和 Explainability 模型之间的区别以及为什么它可能不那么重要 当你第一次深入可解释机器学习领域时&#xff0c;你会…

第十四届蓝桥杯省赛C++B组G题【子串简写】题解(AC)

题目大意 给定字符串 s s s&#xff0c;字符 a , b a, b a,b&#xff0c;问字符串 s s s 中有多少个 a a a 开头 b b b 结尾的子串。 解题思路 20pts 使用二重循环枚举左端点和右端点&#xff0c;判断是否为 a a a 开头 b b b 结尾的字符串&#xff0c;是则答案加一…

MyBatis1(JDBC编程和ORM模型 MyBatis简介 实现增删改查 MyBatis生命周期)

目录 一、JDBC编程和ORM模型 1. JDBC回顾 2. JDBC的弊端 3. ORM模型 Mybatis和hibernate 区别: 4. mybatis 解决了jdbc 的问题 二、MyBatis简介 1. MyBatis快速开始 1.1 导入jar包 1.2 引入 mybatis-config.xml 配置文件 1.3 引入 Mapper 映射文件 1.3 测试 …

构建滑块组件_第 2 部分

本篇我们继续学习滑块组件&#xff0c;让我们把滑块组件构建的更好&#xff1b; ● 首先&#xff0c;我们想要获取组件的三个点&#xff0c;首先在获取到他的HTML元素 const dotContainer document.querySelector(.dots);● 接着遍历 slides 数组&#xff0c;并在动态创建 元…

【MySQL】锁(黑马课程)

【MySQL】锁 0. 锁的考察点1. 概述1. 锁的分类1.1 属性分类1.2 粒度分类 2. 全局锁2.1 全局锁操作2.2.1 备份问题 3. 表级锁3.1 表锁3.2 语法3.3 表共享读锁&#xff08;读锁&#xff09;3.4 表独占写锁&#xff08;写锁&#xff09;3.5 元数据锁(meta data lock, MDL)3.6 意向…

分享实现地铁车辆侧面图

简介 通过伪类和关键帧动画实现地铁车辆侧面图 在线演示 伪元素和关键帧动画 实现代码 <!DOCTYPE html><html><head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <meta http-equiv"X-UA-Co…

【免费资料】IEEE33节点系统参数及拓扑图visio

主要内容 对于初学配电网的同学&#xff0c;最经典的系统即是33节点配电网系统&#xff0c;在各个研究文献中出现频次最高的也是这个系统&#xff0c;为了让大家更好了解33节点系统参数&#xff0c;本次整理了系统节点、支路参数excel以及33节点网络拓扑图visio&#xff0c…

org.springframework.jdbc.BadSqlGrammarException异常

Bug 记录 概述 在执行定时任务更新电子书统计信息时&#xff0c;遇到了 org.springframework.jdbc.BadSqlGrammarException 异常&#xff0c;具体表现为 SQL 函数 count 被错误地解析为自定义函数 wiki.count&#xff0c;导致数据库更新操作失败。 详细描述 错误信息&#x…

adb不插usb线通过wifi调试

说起做手机开发也有好多年了&#xff0c;说来惭愧&#xff0c;我最近才知道安卓手机是可以不插数据线进行开发调试的。起因是公司近期采购了一批安卓一卡通设备&#xff0c;需要对其进行定制开发APP,但是由于我插USB调试发现没有反应。通过询问厂家才知道可以通过WIFI进行调试。…

Gradient Descent

在整个maching learning的第三个步骤要找一个最好的function。在第二步是定义了一个 Loss function L&#xff0c;这个L是一个function的fuction 求完偏微分之后得到的向量就是Gradient&#xff08;黄色部分&#xff09; 随机找一个起始点0&#xff0c;它的等高线的法线方向就…

Flash存储器解析:从原理到应用,全面了解其与缓存的区别

Flash存储器解析&#xff1a;从原理到应用&#xff0c;全面了解其与缓存的区别 Flash存储器是一种非易失性存储器技术&#xff0c;广泛应用于各种电子设备中&#xff0c;如USB闪存盘、固态硬盘&#xff08;SSD&#xff09;、智能手机、数码相机和嵌入式系统。它能够在断电情况下…

Windows使用nxlog发送系统日志到Linux的rsyslog服务器

Windows使用nxlog发送系统日志到Linux的rsyslog服务器 前言一、IP地址规划及示意图二、在windows上安装及配置nxlog1.下载nxlog2.安装nxlog3.配置nxlog4.创建对应日志路径的文件夹 三、windows上启动nxlog服务四、在CentOS 7上配置日志存到指定位置文件1.编辑/etc/rsyslog.conf…