【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(二)

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:贪心算法篇–CSDN博客

在这里插入图片描述

文章目录

  • 前言
  • 例题
    • 1.买卖股票的最佳时机
    • 2.买卖股票的最佳时机2
    • 3.k次取反后的最大化数组和
    • 4.按身高排序
    • 5.优势洗牌
    • 6.最长回文串
    • 7.增减字符串匹配
    • 8.分发饼干

前言

本篇文章是对贪心算法练习题的讲解,有关贪心算法的讲解可以看本系列的第一篇文章,这里就不再重复讲解,直接继续讲解例题。

例题

1.买卖股票的最佳时机

题目

在这里插入图片描述

算法原理

本道题的贪心策略很简单,既然要求获取最高的利润,而且本道题限制只能买卖一次,那么就在整个股票价格中选择最低点买入,最高点卖出即可,这样就能保证利润最大化。

设置两个变量,一个用来标记最低的买价,一个用来标记当前利润;遍历整个股票价格,现将当前价格减去最低的买价更新利润,然后更新当前价格是否是最低价。遍历完整个股票价格后,当前利润就是最大的利润。

代码实现

int maxProfit(vector<int>& prices){//minnum表示最低的买价,ret表示最高的利润int minnum = INT_MAX, ret = INT_MIN;for(auto x : prices){ret = max(ret, x - minnum);minnum = min(minnum, x);}if(ret<0){return 0;}return ret;
}

2.买卖股票的最佳时机2

题目

在这里插入图片描述

算法原理

本道题和上一道题不同的是,不再局限于只能买卖一次,可以无限次买卖,只要保证最后的利润是最大的即可。

首先需要明白,什么时候买卖才能有利润不亏损,那就是下一个价格比当前价格低,也就是升序排列,所以在上升趋势段买卖,才会有利润,但是在上升趋势段的什么时候买卖才能最大利润,那肯定就是上升趋势段的最低点和最高点买入和卖出。因此找到所有升序段的利润然后相加就是整体的最大利润。这就是本道题的贪心策略。

这里有两种解法:第一种就是找到一个升序段,标记最低点和最高点的价格,然后直接两个端点相减就是当前整个升序段的利润和;第二种就是只要下一个的价格大于当前价格,就买入和卖出。比如说当前有连续三天的价格都是上升趋势,第一种方式就是在第一天买入,第三天卖出,只找开始和结尾。而第二种方式则是,第一天买入,第二天卖出,然后第二天再买入,第三天卖出,将整个升序段的利润拆分成每一天利润,最后每一天的利润和还是整个升序段的利润和。

代码实现

//方法一:
int maxProfit(vector<int>& prices){//使用双指针找到上升趋势的最低点和最高点进行买卖int ret = 0;for (int buy = 0; buy < prices.size(); buy++){//卖指针从买指针位置开始找int sell = buy;//找到上身趋势的最高点while(sell+1<prices.size()&&prices[sell]<prices[sell+1]){sell++;}//在当前位置买卖ret += prices[sell] - prices[buy];//更新买指针指向当前位置buy = sell;}return ret;
}
//方法二:
int maxProfit(vector<int>& prices){//在上升趋势段,一天一天地买卖获取利润int ret = 0;for (int i = 0; i < prices.size(); i++){//如果下一个值大于当前值,就直接买卖if(i+1<prices.size()&&prices[i+1]>prices[i]){ret += prices[i + 1] - prices[i];}}return ret;
}

3.k次取反后的最大化数组和

题目

在这里插入图片描述

算法原理

本道题的题意要求,挑选数组中的一个数变化为相反数,总共需要变化k次,最后使变化后的数组和最大。注意一个数可以变化多次,没有次数限制。

既然题意要求变化后的数组和最大,那么每次变化都挑选最小的数变化不就行了吗,这样就能对数组和的影响减小。但是数组中有正数和负数之分,因此两种情况要分类讨论:如果是负数的话,负数的相反数是正数,可以使数组和变大,因此负数只需变化一次即可;如果是正数的话,还要继续分情况讨论:如果当前剩余的k次为偶数,正数变化两次后还是正数,所以变化偶数次后还是当前数,相当于不变,对数组和无影响直接结束;如果当前剩余的k次为奇数,只需让最小的正数变化一次为负数,这样对数组和的影响最小,然后剩余偶数次,变化偶数次后相当于不变,直接结束即可。这就是本道题的贪心策略。

这里我是用小根堆数据结构来实现,每次直接获取堆顶元素,然后判断正负,负数就变化一次,变化后存放到堆中,正数再判断剩余次数的奇偶,如果是偶数,直接结束,相当于变化偶数次不变;如果是奇数,当前正数因为是最小的正数所以变化一次,剩余偶数次,同理直接结束。最后返回变化后的元素和

代码实现

struct Greater{bool operator()(const int p1,const int p2){return p1 > p2;}
};
int largestSumAfterKNegations(vector<int>& nums, int k){//建立一个小根堆priority_queue<int, vector<int>, Greater> heap(nums.begin(), nums.end());while(k>0){//获取堆顶元素int t = heap.top();heap.pop();//如果堆顶元素是负数,修改为正数if(t<0){heap.push(-t);k--;}//如果堆顶元素是正数,先判断当前剩余次数else{//如果次数为奇数,修改最小的正数为负数,次数减一,剩余次数就是偶数//下一个正数进行偶数次的变换,相当于不变,直接结束if(k%2==1){heap.push(-t);k = 0;}//如果剩余偶数次,当前正数变化完所有次数,相当于不变,直接结束else{heap.push(t);k = 0;}}}int ret = 0;while(!heap.empty()){ret+=heap.top();heap.pop();}return ret;
}

4.按身高排序

题目

在这里插入图片描述

算法原理

本道题其实并不是贪心算法题,但是本道题使用的策略会在下一道贪心算法题中用到,因此可以先用这道题练习一下。

本道题就是一道简单的排序问题,一个数组存放的是名字,一个数组存放的是身高,两个数组通过下标可以一一对应名字和身高,然后就是按照身高排序,返回排序后的名字。这里就存在了一个细节问题,如果直接对身高这个数组排序,排完序后,名字数组没有变,就会导致不能再通过下标一一对应名字和身高,最后返回的名字顺序也就是错误的。

本道题有多种解法,第一种就是可以使用二元数组,重新创建一个数组,数组里面存放的是键值对pair<string,int>,使每个名字一一对应各自的身高。然后将数组按照身高排序,最后遍历数组返回名字即可。第二种就是可以借助哈希表,建立名字和身高的映射关系,其实和第一种同理,只不过数据存储方式不一样,实现过程还是相同的。

上面两种方式都可以实现,但这里重点要介绍的还是第三种方式:直接对下标进行排序,建立一个新数组,存放的是每一个下标,然后通过Lambda表达式自定义排序规则,[&]是捕获列表,用于排序时可以访问的外部变量,这里排序的外部变量就是身高数组;()内是参数列表,对应下标数组中需要排序的两个下标;{}内是函数体,也就是排序规则的实现,按照下标对应的身高数组中的身高进行排序。这样自定义排序规则就可以实现下标按照身高排序,不改变原有的名字和身高数组,最后再遍历排序好的下标数组,通过下标就可以找到名字,返回即可。

代码实现

vector<string> sortPeople(vector<string>& names, vector<int>& heights){//建立一个下标数组int n = names.size();vector<int> index(n);for (int i = 0; i < n; i++){index[i] = i;}//对下标数组进行排序sort(index.begin(), index.end(), [&](const int i, const int j){return heights[i] > heights[j]; });//提取结果vector<string> ret;for(auto i : index){ret.push_back(names[i]);}return ret;
}

5.优势洗牌

题目

在这里插入图片描述

算法原理

在这里插入图片描述

代码实现

vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2){int n = nums1.size(), m = nums2.size();//给数组2创建一个下标数组vector<int> index(m);for (int i = 0; i < m; i++){index[i] = i;}// 数组1直接排序,数组2通过下标数组排序,排升序sort(nums1.begin(), nums1.end());sort(index.begin(), index.end(), [&](const int i, const int j) {return nums2[i] < nums2[j]; });//创建一个结果数组用来存放数组1按题意要求的排序vector<int> ret(n);//两个指针用来指向下标数组的前后位置int left = 0, right = m - 1;for (int i = 0; i < ; i++){//如果第一个数组的值大于第二个数组的当前值,左指针指向下标数组中的位置就是结果数组中存放的下标if(nums1[i]>nums2[index[left]]){ret[index[left++]] = nums1[i];}//如果小于等于,存放到右指针指向的下标位置else{ret[index[right--]] = nums1[i];}}return ret;
}

6.最长回文串

题目

在这里插入图片描述

算法原理

本道题的贪心策略比较简单,构成回文串时尽可能多地选取字符,先统计所有字符的个数,有奇数个也有偶数个,如果当前字符是偶数个n,那么回文串中一定包含所有的当前字符n个,比如字符a有4个,回文串就是aa|aa,左右两侧平均分;如果当前字符是奇数个n,那么回文串中一定包含当前字符的n-1个,比如字符b有5个,回文串就是bb|bb;当统计完所有字符后,如果最后回文串的长度小于原字符串的长度,说明存在奇数个字符,随便选一个奇数个的字符放到中间|位置,回文串的长度再加一。

代码实现

int longestPalindrome(string s){//建立哈希表,统计字符的个数unordered_map<char, int> hash;for(auto ch : s){hash[ch]++;}int ret = 0;for(auto& [ch,count] : hash){//不管个数是奇数还是偶数,都是先除以2再乘以2ret += count / 2 * 2;}//如果最后回文串的长度小于原字符串的长度,说明存在奇数个的字符,回文串长度再+1if(ret<s.size()){ret++;}return ret;
}

7.增减字符串匹配

题目

在这里插入图片描述

算法原理

本道题的贪心策略:原字符串的长度为n,从0到n挑选数字填充数组对应字符串中的每个字符,遇到’I’选择0到n中剩余的最小的数,因为下一个不管位置是字符’I’还是字符’D’,挑选的值都会比当前最小的数大,这样就能满足字符’I’的要求,下一个比当前的大;遇到’D’选择0到n中剩余的最大的数,因为下一个不管位置时字符’I’还是字符’D’,挑选的值都会比当前最大的数小,这样就能满足字符’D’的要求,下一个比当前的小,当遍历完所有的字符后,剩下的一个值填充到数组最后。

代码实现

vector<int> diStringMatch(string s){//贪心策略:遇到'I'选择最小的数,遇到'D'选择最大的数int n = s.size();//两个指针表示0到n的数字,left表示最小的数,right表示最大的数int left = 0, right = n;vector<int> ret(n + 1);for (int i = 0; i < n; i++){if(s[i]=='I'){ret[i] = left++;}else{ret[i] = right--;}}//最后一个位置用剩下的一个值填充ret[n] = left;return ret;
}

8.分发饼干

题目

在这里插入图片描述

算法原理

本道题的贪心策略其实和上面优势洗牌一样,同样借助了田忌赛马的原理,只不过本道题比较简单,要求统计的是最多能满足的个数,不用按照数组的格式一一对应返回,所以贪心策略就可以简单化,还是先将两个数组排序然后一一比较,如果两个数能比过,也就是数组s的值大于数组g的值就直接比较下一对,满足个数加一;如果比不过,也就是数组s的值小于数组g的值,不用再和数组g中最大的值比较去拖累最大的值,因为即使和最大的比较也比过,题意要求返回的是满足个数,所以直接跳过当前数组s的值,用下一个比即可。

相较于优势洗牌那道题,本道题还是比较简单的。

代码实现

int findContentChildren(vector<int>& g, vector<int>& s){//贪心策略,先将两个数组排序,在能满足当前胃口的情况下选择较小的饼干sort(g.begin(),g.end());sort(s.begin(), s.end());int ret = 0;for (int i = 0, j = 0; i < g.size() && j < s.size(); ){//如果当前饼干能满足当前胃口值,直接选择当前饼干if(s[j]>=g[i]){ret++;i++;j++;}//否则,跳过当前饼干else{j++;}}return ret;
}

以上就是关于贪心算法练习题第二部分的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

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

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

相关文章

unity学习25:用 transform 进行旋转和移动,简单的太阳地球月亮模型,以及父子级关系

目录 备注内容 1游戏物体的父子级关系 1.1 父子物体 1.2 坐标关系 1.3 父子物体实际是用 每个gameobject的tranform来关联的 2 获取gameObject的静态数据 2.1 具体命令 2.2 具体代码 2.3 输出结果 3 获取gameObject 的方向 3.1 游戏里默认的3个方向 3.2 获取方向代…

虚幻基础17:动画层接口

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 animation layer interface animation layer interface 动画层接口&#xff1a;动画图表的集。仅有名字。 添加到动画蓝图中&#xff0c;由动画蓝图实现动画图表。

位运算的概念

文章目录 整数在计算机中的表示二进制表示有符号类型和无符号类型机器数和真值原码、反码和补码原码、反码和补码的表示方法计算机中的表示 位运算与、或、异或和取反移位运算移位运算与乘除法的关系位运算的性质 目录 整数在计算机中的表示 二进制表示 程序中的所有数在计算…

1/30每日一题

从输入 URL 到页面展示到底发生了什么&#xff1f; 1. 输入 URL 与浏览器解析 当你在浏览器地址栏输入 URL 并按下回车&#xff0c;浏览器首先会解析这个 URL&#xff08;统一资源定位符&#xff09;&#xff0c;比如 https://www.example.com。浏览器会解析这个 URL 中的不同…

基于深度学习的视觉检测小项目(十七) 用户管理后台的编程

完成了用户管理功能的阶段。下一阶段进入AI功能相关。所有的资源见文章链接。 补充完后台代码的用户管理界面代码&#xff1a; import sqlite3from PySide6.QtCore import Slot from PySide6.QtWidgets import QDialog, QMessageBoxfrom . import user_manage # 导入使用ui…

每日 Java 面试题分享【第 20 天】

欢迎来到每日 Java 面试题分享栏目&#xff01; 订阅专栏&#xff0c;不错过每一天的练习 今日分享 3 道面试题目&#xff01; 评论区复述一遍印象更深刻噢~ 目录 问题一&#xff1a;什么是 BIO、NIO、AIO&#xff1f;问题二&#xff1a;什么是 Channel&#xff1f;问题三&…

如何解决云台重力补偿?

如何解决云台重力补偿? 最近在调试步兵云台的时候,由于枪管、图传、摄像头等重力的原因,pitch轴的参数尤其难以调整,又不想抬升和降低使用两套不同的参数,所以使用了重力补偿,效果也是比较理想的,于是整理为一篇文章记录一下 一、问题根源:枪管重力在“搞事情” 想象…

Vue指令v-html

目录 一、Vue中的v-html指令是什么&#xff1f;二、v-html指令与v-text指令的区别&#xff1f; 一、Vue中的v-html指令是什么&#xff1f; v-html指令的作用是&#xff1a;设置元素的innerHTML&#xff0c;内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…

软考高项笔记 信息技术及其发展

信息技术及其发展 ❝ 信息系统项目管理师第二章第一节 1. 网络标准协议的定义 网络协议是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。网络协议由三个要素组成&#xff0c;分别是语义、语法和时序。 语义&#xff1a;解释控制信息每个部分的含义&#xff0c;它…

【C++ STL】vector容器详解:从入门到精通

【C STL】vector容器详解&#xff1a;从入门到精通 摘要&#xff1a;本文深入讲解C STL中vector容器的使用方法&#xff0c;涵盖常用函数、代码示例及注意事项&#xff0c;助你快速掌握动态数组的核心操作&#xff01; 一、vector概述 vector是C标准模板库&#xff08;STL&am…

深入解析 Chrome 浏览器的多进程架构:标签页是进程还是线程?(中英双语)

深入解析 Chrome 浏览器的多进程架构&#xff1a;标签页是进程还是线程&#xff1f; 1. 引言 Google Chrome 作为全球最流行的浏览器之一&#xff0c;以其稳定性、安全性和多任务处理能力而闻名。而其高效的表现&#xff0c;很大程度上归功于其独特的多进程架构&#xff08;M…

模型蒸馏(ChatGPT文档)

文章来源&#xff1a; https://chatgpt.cadn.net.cn/docs/guides_distillation 模型蒸馏 使用蒸馏技术改进较小的模型。 模型蒸馏允许您利用大型模型的输出来微调较小的模型&#xff0c;使其能够在特定任务上实现类似的性能。此过程可以显著降低成本和延迟&#xff0c;因为较小…

树莓派pico入坑笔记,触摸引脚

这里主要是扒一扒官方是如何实现触摸引脚的功能的&#xff0c;rp2040本身是不支持触摸传感的&#xff0c;那么官方是怎么实现的呢&#xff0c;这件事一直困扰着我&#xff0c;在官方给出的使用中&#xff0c;要求外接一颗至少为1M欧姆的电阻下拉&#xff0c;然后就可以使用触摸…

deepseek本地部署+结合思路

deepseek本地部署 配置&#xff1a; 建议配置 运行内存16GB 显卡&#xff1a;4060 操作系统&#xff1a;win11/win10 存储&#xff1a;512GB 一、安装Python 3.11环境&#xff08;参见&#xff09; 超详细的Python安装和环境搭建教程_python安装教程-CSDN博客 二、安装…

加载数据,并切分

# Step 3 . WebBaseLoader 配置为专门从 Lilian Weng 的博客文章中抓取和加载内容。它仅针对网页的相关部分&#xff08;例如帖子内容、标题和标头&#xff09;进行处理。 加载信息 from langchain_community.document_loaders import WebBaseLoader loader WebBaseLoader(w…

AI(计算机视觉)自学路线

本文仅用来记录一下自学路线方便日后复习&#xff0c;如果对你自学有帮助的话也很开心o(*&#xffe3;▽&#xffe3;*)ブ B站吴恩达机器学习->B站小土堆pytorch基础学习->opencv相关知识&#xff08;Halcon或者opencv库&#xff09;->四类神经网络&#xff08;这里跟…

计算机组成原理——存储系统(二)

&#x1f331; "人生最深的裂痕&#xff0c;往往是光照进来的地方。 别怕脚下的荆棘&#xff0c;那是你与平庸划清界限的勋章&#xff1b;别惧眼前的迷雾&#xff0c;星辰永远藏在云层之上。真正的强者不是从未跌倒&#xff0c;而是把每一次踉跄都踏成攀登的阶梯。记住&am…

解锁豆瓣高清海报(二) 使用 OpenCV 拼接和压缩

解锁豆瓣高清海报(二): 使用 OpenCV 拼接和压缩 脚本地址: 项目地址: Gazer PixelWeaver.py pixel_squeezer_cv2.py 前瞻 继上一篇“解锁豆瓣高清海报(一) 深度爬虫与requests进阶之路”成功爬取豆瓣电影海报之后&#xff0c;本文将介绍如何使用 OpenCV 对这些海报进行智…

OSCP - Proving Grounds - Roquefort

主要知识点 githook 注入Linux path覆盖 具体步骤 依旧是nmap扫描开始&#xff0c;3000端口不是很熟悉&#xff0c;先看一下 Nmap scan report for 192.168.54.67 Host is up (0.00083s latency). Not shown: 65530 filtered tcp ports (no-response) PORT STATE SERV…

记忆化搜索和动态规划 --最长回文子串为例

记忆化搜索 记忆化搜索是一种优化递归算法的方法&#xff0c;通过将已经计算过的子问题的结果存储起来&#xff08;通常使用哈希表或数组&#xff09;&#xff0c;避免重复计算相同的子问题。 本质上是通过缓存中间结果来减少计算的重复性。 动态规划 动态规划是通过将问题分…