代码随想录算法训练营第二十七天(贪心 一)

news2024/9/26 5:21:30

硬拖拖到现在才写完。。。

关于贪心:

文章链接: 代码随想录

文章摘要:

贪心的本质是选择每一阶段的局部最优,从而达到全局最优。

贪心算法并没有固定的套路

和其他算法不同,贪心没有能看出局部最优是否能推出整体最优的通法。

用来验证可不可以用贪心算法的最好策略就是举反例,如果想不到反例,那么就试一试贪心吧

面试中基本不会让面试者现场证明贪心的合理性,代码写出来跑过测试用例即可,或者自己能自圆其说理由就行了

很多同学通过(accept)了贪心的题目,但都不知道自己用了贪心算法,因为贪心有时候就是常识性的推导,所以会认为本应该就这么做!

看起来这算法挺玄乎,那么来几道题感受感受吧!

力扣题:

455.分发饼干

题目链接: . - 力扣(LeetCode)

题面:

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

思路:

一下子没想出来,看完觉得其实挺简单的:

为了满足更多的小孩,就不要造成饼干尺寸的浪费。

大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的。

看到这里相信有点思路了吧?

局部最优:大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个

全局最优:喂饱尽可能多的小孩

先将饼干数组和小孩数组排序,然后用大饼干优先满足胃口大的,并统计满足小孩数量。

用两个下标表示读取饼干和小孩数组的顺序,如果排序从低到高,那就两个下标就从末尾开始读取,从后往前,饼干肯定足够大,孩子胃口也足够大,如果孩子能满足的话就吃下足够大的饼干,此时计数器计数并往下走,如果孩子胃口满足不了,就去往下找能满足胃口的孩子。

这么说可能有点绕,看代码其实还一目了然一些。

代码实现: 

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int count = 0, si = s.size() - 1, gi = g.size() - 1;
        while(si >= 0 && gi >= 0)
        {
            if(g[gi] <= s[si]) si --, gi --, count ++;
            else gi --;
        }
        return count;
    }
};

376. 摆动序列

题目链接:. - 力扣(LeetCode)

题面:

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

思路:

本题要求通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

相信这么一说吓退不少人,这要求最大摆动序列又可以修改数组,这得如何修改呢?

来分析一下,要求删除元素使其达到最大摆动序列,应该删除什么元素呢?

用示例二来举例,如图所示:

局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值

整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列

局部最优推出全局最优,并举不出反例,那么试试贪心!

(为方便表述,以下说的峰值都是指局部峰值)

实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)

这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点

在计算是否有峰值的时候,大家知道遍历的下标 i ,计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i]),如果prediff < 0 && curdiff > 0 或者 prediff > 0 && curdiff < 0 此时就有波动就需要统计。

这是我们思考本题的一个大体思路,但本题要考虑到相连的元素相等的情况(也就是平坡),

 平坡分两种,一个是 上下中间有平坡,一个是单调有平坡,如图:

下面的部分代码有一个值得好好讲的东西:

为什么preDiff = curDiff会在if条件里面呢?按常理来讲preDiff应该实时更新才对,但是如果实时更新下面的图就会得到错误的答案:

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; // 注意这里,只在摆动变化的时候更新prediff
            }
        }

图中,我们可以看出,版本一的代码在三个地方记录峰值,但其实结果因为是 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 ,就是为了 上面我说的这种情况

其实本题看起来好像简单,但需要考虑的情况还是很复杂的,而且很难一次性想到位。

代码实现: 

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.size() <= 1) return nums.size();
        int curDiff = 0; // 当前一对差值
        int preDiff = 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; // 注意这里,只在摆动变化的时候更新prediff
            }
        }
        return result;
    }
};

53. 最大子序和

题目链接:. - 力扣(LeetCode)

题面:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和子数组(子数组 是数组中连续的 非空 元素序列)是数组中的一个连续部分。

思路:

如果要硬找,那就需要两层for循环:

第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值

那这道题如果用贪心贪的是哪里呢?

如果 -2 1 在一起,计算起点的时候,一定是从 1 开始计算,因为负数只会拉低总和,这就是贪心贪的地方!

局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。

全局最优:选取最大“连续和”

局部最优的情况下,并记录最大的“连续和”,可以推出全局最优

从代码角度上来讲:遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。

这相当于是暴力解法中的不断调整最大子序和区间的起始位置

那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?

区间的终止位置,其实就是如果 count 取到最大值了,及时记录下来了:

if (count > result) result = count;

这样相当于是用 result 记录最大子序和区间和(变相的算是调整了终止位置)

代码实现: 

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int count = 0;
        int max = INT32_MIN;
        for(int i = 0; i < nums.size(); i ++)
        {
            count += nums[i];
            if(max < count) max = count;
            if(count <= 0) count = 0;
        }
        return max;
    }
};

关于本代码的一些常见误区:

误区一:

不少同学认为 如果输入用例都是-1,或者 都是负数,这个贪心算法跑出来的结果是 0, 这是又一次证明脑洞模拟不靠谱的经典案例,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。

误区二:

大家在使用贪心算法求解本题,经常陷入的误区,就是分不清,是遇到 负数就选择起始位置,还是连续和为负选择起始位置。

在动画演示用,大家可以发现, 4,遇到 -1 的时候,我们依然累加了,为什么呢?

因为和为 3,只要连续和还是正数就会 对后面的元素 起到增大总和的作用。 所以只要连续和为正数我们就保留。

这里也会有录友疑惑,那 4 + -1 之后 不就变小了吗? 会不会错过 4 成为最大连续和的可能性?

其实并不会,因为还有一个变量 result 一直在更新 最大的连续和,只要有更大的连续和出现,result 就更新了,那么 result 已经把 4 更新了,后面 连续和变成 3,也不会对最后结果有影响。

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

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

相关文章

软件渗透测试必要性简析,第三方软件测试机构如何进行渗透测试?

在信息技术迅速发展的今天&#xff0c;软件渗透测试逐渐成为了确保信息安全的重要环节。软件渗透测试指的是对系统或应用程序进行模拟攻击&#xff0c;以发现其潜在的安全风险与脆弱性。不同于传统的安全审计&#xff0c;渗透测试更注重实际攻击过程和攻击者的视角&#xff0c;…

IO进程线程8月26ri

1&#xff0c;思维导图 2&#xff0c;用两个进程分别复制文件的上下两部分到另一个文件 #include<myhead.h> int main(int argc, const char *argv[]) {int fpopen("./1.txt",O_RDONLY);if(fp-1){perror("open");return -1;}int countlseek(fp,0,SE…

如何在 mind+ 中编写 python 程序

打开Mind&#xff0c;点击窗口右上角的【Python模式】按钮&#xff0c;由实时模式切换到Python模式。 将默认的循环执行模块拖动到左边的模块区删除。 点击【变量】&#xff0c;将【打印【Hello World】】模块拼接到【Python主程序开始】下方。 将【获取输入&#xff0c;提示语…

redis(未授权访问漏洞)

环境准备 下载并安装Redis 首先&#xff0c;下载Redis的源代码包并解压&#xff1a; wget http://download.redis.io/releases/redis-2.8.17.tar.gz tar xzf redis-2.8.17.tar.gz cd redis-2.8.17接着&#xff0c;编译安装Redis&#xff1a; 编译完成后&#xff0c;进入src目录…

自动化任务工具 | zTasker v1.97.1 绿色版

在自动化任务管理领域&#xff0c;一款名为zTasker的软件以其卓越的性能和易用性脱颖而出。今天&#xff0c;电脑天空将为大家详细介绍这款软件的亮点和使用场景。 功能特点 1. 轻量级设计&#xff0c;快速启动 zTasker以其小巧的体积和快速的启动速度&#xff0c;为用户提供…

模型 7S分析法(麦肯锡)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。组织全面诊断&#xff0c;战略协同优化。 1 7S分析法(麦肯锡)的应用 1.1 邮储银行的转型&#xff1a;基于麦肯锡7S模型的竞争力提升 中国邮储银行面临着激烈的金融行业竞争&#xff0c;为了迅速提升…

考研数学 高等数学----导数应用

核心框架 前置知识 正式内容 知识点1: 知识点2: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知…

嵌入式系统课后习题(带答案)

资料截图&#xff08;部分&#xff09;&#xff1a; &#x1f680; 获取更多详细资料可点击链接进群领取&#xff0c;谢谢支持&#x1f447; 点击免费领取更多资料

Ant Design Vue中Modal.confirm无法自动关闭

温馨tips:着急看解决方法可跳过碎碎念~ 前两天经理扔给我一个问题&#xff1a;“这个弹窗怎么关不上了&#xff1f;” 我怀着无所谓的心态&#xff1a;小意思啦&#xff0c;5分钟之内解决完~ …当然flag是不能随便乱立的 拉下来项目&#xff08;原神启动&#xff08;不是&…

@ohos.systemParameterEnhance系统参数接口调用:获取系统属性

在去年的文章&#xff0c;笔者介绍了如何使用修改修改OpenHarmony 设备厂家名称 、硬件版本号 等系统属性&#xff0c;本文介绍一下在应用层怎么获取系统属性。 开发环境 DAYU200 rk3568开发板OpenHarmony 4.1r API 10 (full sdk)DevEco Studio 4.1 Release 开发步骤 1.首先…

浅谈【数据结构】树与二叉树之哈夫曼树

目录 1、哈夫曼树 1.1哈夫曼编码 1.2哈夫曼树 1.3构建一棵哈夫曼树 谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注 没错&#xff0c;说的就是你&#xff0c;不用再怀疑&#xff01;&#xff01;&#xff01; 希望我的文章内容能对你有帮助&#xff0c;一起努力吧…

【FPGA数字信号处理】什么是信号?

在数字信号处理的奇妙世界里&#xff01;“信号”是一切的基础&#xff0c;理解了信号&#xff0c;就相当于拿到了开启数字信号处理大门的钥匙。 今天&#xff0c;就让我们一起深入探究数字信号处理基础中的“信号”。 一、信号的基本概念 信号&#xff0c;简单来说&#xf…

【持续更新】Mχ Plaayer Pro 1.86.0安卓知名播放器最新免费高级修改版

Mχ Plaayer Pro MOD 版本免费 APK&#xff0c;专为安卓手机和平板打造。这是一款功能强大的视频播放器&#xff0c;具备先进的硬件加速技术和字幕支持功能。 • 硬件加速 - 新增 HW 解码器帮助更多视频格式实现硬件加速。 • 多核心解码 - Mχ Plaayer 是首款支持多核心解码的…

链接 -- 动静态链接 --特点、区别、静态库安装下载

1.链接是什么&#xff1f; 我们的程序&#xff0c;和 库&#xff08;语言一定会有自己的标准库&#xff09; 结合的过程就叫做链接。 2.为什么有链接&#xff1f; 让开发站在巨人的肩膀&#xff0c;提高开发效率。 c语言库&#xff1a; ls /user/include/ 动静态库的特点与区别…

领域驱动设计DDD详解与战术建模落地

一、什么是DDD&#xff1f; 1.1、DDD的概念 Domain-Driven Design&#xff08;领域驱动设计&#xff09;它由Eric Evans在他的2003年出版的书籍《Domain-Driven Design: Tackling Complexity in the Heart of Software》中首次提出。DDD 核心思想是通过领域驱动设计方法定义领…

COMSOL工业碱性电解槽(3D、双欧拉模型)

本案例通过改写COMSOL官方案例获得&#xff0c;使用了碱性电解槽和欧拉-欧拉&#xff08;湍流&#xff09;模型&#xff0c;阳极室和阴极室带有乳突状的结构&#xff0c;模型进行了人为缩小&#xff0c;仅供参考。其中一些参数可参考如下链接文章的说明。COMSOL碱性电解槽参数解…

在NextChat中接入SiliconCloud API 体验不同的开源先进大语言模型

NextChat介绍 One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 & Gemini Pro support. 一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。 主要功能 在 1 分钟内使用 Vercel 免费一键部署提供体积…

Cyberchef实用功能之-json解析美化和转换

本文将介绍如何使用cyberchef的json操作功能&#xff0c;根据自己的需求处理常见的json问题。 在网络安全日常的运营&#xff0c;护网行动&#xff0c;重保活动的过程中&#xff0c;经常需要查看多种平台的json格式日志&#xff0c;如下&#xff1a; 以json格式存储的防火墙日…

基于Python Django的保险数据分析系统优化要点

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

畅捷通CRM newleadset.php SQL注入漏洞复现

0x01 产品简介 用友畅捷通CRM是面向小企业全力打造的简单、实用的客户关系管理应用。帮助企业用好自己的客户资源、管好商机跟进过程、引导好业务员跟单行为,促进团队销售能力的提升;通过查询和分析,识别企业的价值客户,融合电话、短信、邮件等工具,实现精准营销;帮助企…