C++算法学习心得七.贪心算法(1)

1.贪心算法理论基础

贪心的本质是选择每一阶段的局部最优,从而达到全局最优贪心算法并没有固定的套路,唯一的难点就是如何通过局部最优,推出整体最优。最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧

贪心算法一般分为如下四步:

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

只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了 

2.分发饼干(455题)

题目描述:

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

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

示例  1:

  • 输入: g = [1,2,3], s = [1,1]
  • 输出: 1 解释:你有三个孩子和两块小饼干,3 个孩子的胃口值分别是:1,2,3。虽然你有两块小饼干,由于他们的尺寸都是 1,你只能让胃口值是 1 的孩子满足。所以你应该输出 1。

这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩

可以尝试使用贪心策略,先将饼干数组和小孩数组排序。

然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量。

一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式

 一定要 for 控制 胃口,里面的 if 控制饼干

class Solution {
public://贪心算法,使用大饼干先满足大胃口的原则来实现int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(),g.end());//对胃口和饼干都进行排序sort(s.begin(),s.end());int index = s.size() - 1;//这里采用下标的形式来实现int result = 0;//定义结果//我们从后向前遍历,实现大饼干满足大胃口的原则for(int i = g.size() - 1;i >= 0;i--){//如果满足了条件,饼干下标减一,结果收集if(index >= 0 && s[index] >= g[i]){result++;index--;}}return result;}
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
class Solution {
public://贪心算法实现,小饼干喂小胃口的人,注意下标的选取就好int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(),g.end());sort(s.begin(),s.end());int index = 0,result = 0;for(int i = 0;i < s.size();i++){if(index < g.size() && g[index] <= s[i]){result++;index++;}}return result;}
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

3. 摆动序列(376题)

题目描述:

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

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3)  是正负交替出现的。相反, [1,4,7,2,5]  和  [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

  • 输入: [1,7,4,9,2,5]
  • 输出: 6
  • 解释: 整个序列均为摆动序列。

贪心算法:

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

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

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

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

本题要考虑三种情况:

  1. 情况一:上下坡中有平坡
  2. 情况二:数组首尾两端
  3. 情况三:单调坡中有平坡

 本题异常情况的本质,就是要考虑平坡, 平坡分两种,一个是 上下中间有平坡,一个是单调有平坡

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {if(nums.size() <= 1)return nums.size();int prediff = 0;//前一对差值int curdiff = 0;//当前一对差值int result = 1;//结果//这里注意,i遍历到nums.size()-1,因为默认数组最右边一个序列有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;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

动态规划:

当前考虑的这个数,要么是作为山峰(即 nums[i] > nums[i-1]),要么是作为山谷(即 nums[i] < nums[i - 1])。

  • 设 dp 状态dp[i][0],表示考虑前 i 个数,第 i 个数作为山峰的摆动子序列的最长长度
  • 设 dp 状态dp[i][1],表示考虑前 i 个数,第 i 个数作为山谷的摆动子序列的最长长度

则转移方程为:

  • dp[i][0] = max(dp[i][0], dp[j][1] + 1),其中0 < j < inums[j] < nums[i],表示将 nums[i]接到前面某个山谷后面,作为山峰。
  • dp[i][1] = max(dp[i][1], dp[j][0] + 1),其中0 < j < inums[j] > nums[i],表示将 nums[i]接到前面某个山峰后面,作为山谷。

初始状态:

由于一个数可以接到前面的某个数后面,也可以以自身为子序列的起点,所以初始状态为:dp[0][0] = dp[0][1] = 1

class Solution {
public:int dp[1005][2];int wiggleMaxLength(vector<int>& nums) {memset(dp,0,sizeof dp);dp[0][0] = dp[0][1] = 1;for(int i = 1;i < nums.size();i++){dp[i][0] = dp[i][1] = 1;for(int j = 0;j < i;j++){if (nums[j] > nums[i]) dp[i][1] = max(dp[i][1], dp[j][0] + 1);}for (int j = 0; j < i; ++j) {if (nums[j] < nums[i]) dp[i][0] = max(dp[i][0], dp[j][1] + 1);}}return max(dp[nums.size() - 1][0],dp[nums.size() - 1][1]);}
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n)

4.最大子序和(53题)

题目描述:

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

  • 输入: [-2,1,-3,4,-1,2,1,-5,4]
  • 输出: 6
  • 解释:  连续子数组  [4,-1,2,1] 的和最大,为  6。

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

class Solution {
public:int maxSubArray(vector<int>& nums) {int result = INT32_MIN;int count = 0;for(int i = 0;i < nums.size();i++){count = 0;for(int j = i;j < nums.size();j++){count += nums[j];result = count > result ? count : result;}}return result;}
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

贪心算法:

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

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

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

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

class Solution {
public://int maxSubArray(vector<int>& nums) {int result = INT32_MIN;//定义一个最小值int count = 0;//这个是总和for(int i = 0;i < nums.size();i++){count += nums[i];//计算总和if(count > result){result = count;//如果总和大于结果就去更新,更新最大值}if(count < 0){count = 0;//重置最大子序列初始位置,小于零就重新定位}}return result;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

 动态规划:

class Solution {
public:int maxSubArray(vector<int>& nums) {if(nums.size() == 0)return 0;vector<int>dp(nums.size(),0);//dp[i]表示包括i之前的最大连续子序列和dp[0] = nums[0];int result = dp[0];for(int i = 1;i < nums.size();i++){dp[i] = max(dp[i-1]+nums[i],nums[i]);//状态转移公式if(dp[i] > result)result = dp[i];//result 保存dp[i]的最大值}return result;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

5.买卖股票的最佳时机 II(122题)

题目描述: 

给定一个数组,它的第  i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

  • 输入: [7,1,5,3,6,4]
  • 输出: 7
  • 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

贪心算法: 

假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。

相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。

此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间

class Solution {
public://把整个利润去拆分成每一个利润的和形式,取最大利润就是求所有正利润就好int maxProfit(vector<int>& prices) {int result = 0;//这里注意i的取值从1开始取for(int i = 1;i < prices.size();i++){result += max(prices[i] - prices[i-1],0);//两个位置的价格差值,和0对比求得正利润即可}return result;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

动态规划:

class Solution {
public:int maxProfit(vector<int>& prices) {// dp[i][1]第i天持有的最多现金// dp[i][0]第i天持有股票后的最多现金int n = prices.size();vector<vector<int>>dp(n,vector<int>(2,0));dp[0][0] -= prices[0];// 持股票for(int i = 1;i < prices.size();i++){dp[i][0] = max(dp[i-1][1] - prices[i],dp[i-1][0]);// 第i天持股票所剩最多现金 = max(第i-1天持股票所剩现金, 第i-1天持现金-买第i天的股票)dp[i][1] = max(dp[i-1][1],dp[i-1][0] + prices[i]);// 第i天持有最多现金 = max(第i-1天持有的最多现金,第i-1天持有股票的最多现金+第i天卖出股票)}return max(dp[n-1][0],dp[n-1][1]);}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

 总结:

贪心算法理论基础:每一阶段的局部最优,得到全局最优的解决方法,无固定套路

分发饼干:大饼干喂胃口大的孩子,全局最优就是需要喂饱更多的小孩,先给饼干和小孩胃口排序,然后从后向前遍历小孩数组,用大饼干来喂饱大胃口小孩,并且统计数量,注意遍历饼干技巧,可以采用下标来自减方式实现分发饼干,这里的Index从后向前,可以大饼干喂大胃口或者小饼干喂小胃口

摆动序列:是指两个相邻的元素之差是正负交替的一组序列,使用贪心算法来实现,注意两端是需要算做一个,我们需要记录前一个和当前的差值,条件判断注意需要确保两个差值的正负不同号即可,pre=cur是关键,进行一组就是关键,

最大子序和:贪心算法这里需要注意一个细节我们最开始需要定义一个最小值,再定义每个数组和去,思想逻辑是如果数组和是小于0我们抛弃这个数组和置为0,还要更新最大数组和的一个操作

买卖股票的最佳时机II:我们只要想到利润可以拆分成多个利润相加的模式就可以解决,只要正利润就好,得到最后想要的结果,可以使用动态规划来实现。

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

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

相关文章

leetcode209长度最小的子数组|滑动窗口算法详细讲解学习

滑动窗口是一种基于双指针的一种思想&#xff0c;两个指针指向的元素之间形成一个窗口。 分类&#xff1a;窗口有两类&#xff0c;一种是固定大小类的窗口&#xff0c;一类是大小动态变化的窗口。 简而言之&#xff0c;滑动窗口算法在一个特定大小的字符串或数组上进行操作&…

DevEco Studio 保存自动格式化代码

目标&#xff1a;保存后自动格式化代码 单次快捷键&#xff1a;Ctrl Alt L 步骤一 步骤二

7.2、子集求和问题与背包密码系统

7.2、子集求和问题与背包密码系统 一、数学描述 1.1、第一种描述 20 世纪 70 年代末&#xff0c;默克尔和赫尔曼首次尝试将密码系统建立在一个 NP-完全问题上。他们使用了以下数学问题的一个版本&#xff0c;该问题是对经典knapsack问题的概括。 子集和问题 假设你有一个正…

【Midjourney】AI绘画案例(1)龙年吉祥神兽

说明&#xff1a; 1、文中图片版权均为Midjourney所有&#xff0c;请勿用作商业用途。 2、文中图片均经过 Upscale x 4 处理。 3、由于模型原因&#xff0c;某些图片存在暇玼。 1、吉祥神兽——天马&#xff08;独角兽&#xff09; 天马消灾星。 提示词 Prompt: Sky Unicor…

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…

写一个C++工具类

C工具类是一种封装了一组相关功能的类&#xff0c;用于提供常用的工具函数或方法。下面是编写C工具类的一般步骤&#xff1a; 1. 定义类&#xff1a;首先&#xff0c;需要定义一个类来表示工具类。可以选择将其定义为静态类&#xff0c;这样就不需要创建对象来使用工具函数。 …

Prometheus+grafana配置监控系统

使用docker compose安装 方便拓展, 配置信息都放在在 /docker/prometheus 目录下 1.目录结构如下 . ├── conf │ └── prometheus.yml ├── grafana_data ├── prometheus_data └── prometheus_grafana.yaml2.创建目录文件 mkdir /docker/prometheus &&am…

【unity小技巧】FPS简单的射击换挡瞄准动画控制

文章目录 射击动画控制换弹动画瞄准动画完结 射击动画控制 换弹动画 调用 瞄准动画 问题&#xff1a;瞄准时&#xff0c;但是动画会卡住&#xff0c;不会播放瞄准的待机动画 修改 调用 动画如果太快可以去修改播放速度 播放速度变慢了&#xff0c;可能导致切换待机动画也…

在CentOS 7上安装MySQL 8.0

步骤1&#xff1a;添加MySQL官方Yum仓库 首先&#xff0c;需要导入MySQL的GPG密钥并添加Yum仓库到系统中&#xff1a; # 导入MySQL官方GPG密钥&#xff1a; rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022# 添加MySQL 8.0社区版的Yum仓库&#xff08;替换为对应…

【PyQt】01-PyQt下载

文章目录 前言静态库 一、PyQt是什么&#xff1f;二、安装1.Windows环境下安装安装PyQt5Designer 2.Liunx环境下安装 总结 前言 拜吾师 PyQt5 快速入门 静态库 补充一点知识&#xff1a; Windows&#xff1a; .lib Linux: .a .so(动态库) 简单描述PyQt就是python调用C的Qt文…

《区块链简易速速上手小册》第4章:区块链与加密货币(2024 最新版)

文章目录 4.1 比特币与区块链4.1.1 比特币基础4.1.2 比特币交易的工作流程&#xff1a;4.1.3 拓展案例 1&#xff1a;闪电网络4.1.4 拓展案例 2&#xff1a;比特币ATM 4.2 其他主要加密货币4.2.1 加密货币的多样性4.2.2 以太坊的案例4.2.3 拓展案例 1&#xff1a;非同质化代币&…

什么是实时数据库

1、什么是实时数据库 怎么解释实时数据库&#xff0c;&#x1f634; &#xff0c;先不用下定义且往下看。 2、实时数据库是怎样产生的 试想一下这样的场景&#xff0c;有1000个传感器&#xff08;温度、压力、流量等&#xff09;需要每秒钟都采集所有传感器的数据&#xff0…

郑庆科老师字体正版商用官方授权,字觅网为您提供官方授权渠道

近日,字觅网宣布成为郑庆科老师字体的签约字体品牌,并提供了官方正版商用授权渠道。庆科字体以其独特的风格和设计备受欢迎,现在通过字觅网,用户可以轻松获取该字体的正版商用授权。 用户只需访问庆科字体官网,即可查看庆科字体的详细列表,并选择所需字体进行下载。 在字觅网…

滇西科技师范学院食堂大宗物资采购项目(冰冻制品类)招标公告

滇西科技师范学院食堂大宗物资采购项目(冰冻制品类)招标公告 (招标编号&#xff1a;YDZOH20240158) 项目所在地区&#xff1a;云南省,临沧市,市辖区 一、招标条件 本滇西科技师范学院食堂大宗物资采购项目(冰冻制品类)已由项目审批/核准/备案机关批准&#xff0c;项目资金来源为…

Unity3d Shader篇(一)— 顶点漫反射着色器解析

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、顶点漫反射着色器是什么&#xff1f;1. 顶点漫反射着色器的工作原理 二、编写顶点漫反射着色器1. 定义属性2. 创建 SubShader3. 编写着色器程序段4. 完成顶…

《Pandas 简易速速上手小册》第5章:Pandas 数据合并与重塑(2024 最新版)

文章目录 5.1 数据合并&#xff1a;Concatenate 和 Merge5.1.1 基础知识5.1.2 重点案例&#xff1a;客户订单数据合并5.1.3 拓展案例一&#xff1a;产品目录和销售数据合并5.1.4 拓展案例二&#xff1a;员工信息和部门数据合并 5.2 数据透视和重塑5.2.1 基础知识5.2.2 重点案例…

如何使用Cloudreve搭建私有云盘并发布公网访问无需购买域名服务器

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#…

路由重定向和别名

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介1. 路由重定向实例场景&#xff1a;路由重定向的应用场景&#xff1a; 2. 路由别名实例场景&#xff1a;路由别名的应用场景&#xff1a; ⭐ 写在最后 ⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vue 技能树参考资料专栏&…

备战蓝桥杯---数据结构与STL应用(进阶2)

本文将主要围绕有关map的今典应用展开&#xff1a; 下面我用图进行分析&#xff1a; 下面为AC代码&#xff1a; #include<bits/stdc.h> using namespace std; struct Point {int x,y;bool operator < (const Point & r) const {return x < r.x || ( x r.x &a…

Windows IIS服务如何配置并制作web站点结合内网穿透实现公网访问

文章目录 1. 安装IIS必要WebDav组件2. 客户端测试3. cpolar内网穿透3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访问测试 4. 安装Raidrive客户端4.1 连接WebDav服务器4.2 连接成功4.2 连接成功总结&#xff1a; 自己用Windows Server搭建了家用NAS主机&…