贪心算法的“左最优“与“右最优“及其对应的堆处理和预处理方法

1 答疑

1.1 什么是贪心算法的"左最优"与"右最优"

"左最优"和"右最优"是贪心算法中的两种策略:

  1. 左最优 (Leftmost Greedy): 在每一步选择中,总是选择最左边(最早出现的)可行的选项。

  2. 右最优 (Rightmost Greedy): 在每一步选择中,总是选择最右边(最晚出现的)可行的选项。

这两种策略是贪心算法中根据具体问题选择的不同方向。

1.2 这两种最优问题,分别用什么方法解决?

一般左最优可以采取边遍历边找出"当前的最值", 右最优不能向左最优一样通过左到右的遍历稍带最值,所以一般需要预处理从右到左遍历并且将得到的最值存放到数组中。

2 “左最优” 类题目【也称为反悔贪心/返回堆解法】

左最优其实也是一种局部最优

2.1 字节笔试题[LCP 30. 魔塔游戏] 反悔堆问题

小明在玩一场通关游戏,初始血量为1,关卡有怪兽或者有血包(正数就是血包可回血数,负数说明是怪兽的伤害值),当捡到血包时会加血量,碰到怪兽时会掉血,现在指定初始血量为x,关卡是一个数组,小明必须按照数组的顺序玩游戏,当碰到一个怪兽时,他可以选择将这个怪兽扔到数组末尾,小明可以无限次地将怪兽移到数组末尾,问小明最少移动几次就能存活,如果无论怎么移动都不能存活则返回-1, 假设关卡是这样的[-200,-300,400],则返回-1,假如是这样的[200,100,-250,-60,-70,100],则返回1,只需要把-250挪到尾部,

思路:当发现自己血量不足时,就从当前已经遍历过的所有关卡中,选择耗费血量最多的那个关卡并且放到最后一关,如果即使这样挪开了耗血量最大的一关自身血量还是为负,则直接返回-1,说明无法通关

———————————————— 版权声明:本文为CSDN博主「xxx_520s」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yxg520s/article/details/131989933

本题对应LC的LCP 30. 魔塔游戏

    public int magicTower(int[] nums) {int n=nums.length;PriorityQueue<Integer>pq=new PriorityQueue<>();long sum=0l;for(int i=0;i<n;i++){sum+=nums[i];}if(sum<0){return -1;}int ans=0;sum=0l;for(int i=0;i<n;i++){sum+=nums[i];if(nums[i]<0)pq.offer(nums[i]);if(sum<0){Integer poll=pq.poll();ans++;sum-=(long)poll;}}return ans;}

2.2 leetcode 1642. 可以到达的最远建筑【反悔堆问题】

这样的贪心思路,871题最低加油次数和630课程表III也有体现。

两种贪心策略均可:
优先使用梯子,梯子不够时选取差值最小的出堆改用砖头。(小根堆)
优先使用砖头,砖头不够时选取消耗最大的出堆改用梯子。(大根堆)
解法一的理论复杂度应该是最低的。

class Solution {// 优先使用梯子,梯子不够时选取差值最小的出堆改用砖头。(小根堆)public int furthestBuilding(int[] heights, int bricks, int ladders) {int n = heights.length, sum = 0;Queue<Integer> queue = new PriorityQueue<>();for(int i = 1; i < heights.length; i++) {int diff = heights[i] - heights[i - 1];if(diff > 0) {queue.offer(diff);if(queue.size() > ladders) {sum += queue.poll();}if(sum > bricks)return i - 1;}}return n - 1;}// 优先使用砖头,砖头不够时选取消耗最大的出堆改用梯子。(大根堆)public int furthestBuilding2(int[] heights, int bricks, int ladders) {int n=heights.length;PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->((int)o2-(int)o1));int suml=0,sumb=0;for(int i=1;i<n;i++){int diff=heights[i]-heights[i-1];if(diff>0){pq.add(diff);sumb+=diff;if(sumb>bricks){sumb-=pq.poll();suml++;}if(suml>ladders){return i-1;}}}return n-1;}
}作者:onion12138
链接:https://leetcode.cn/problems/furthest-building-you-can-reach/description/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.3 LC871. 最低加油次数【可对比LC45. 跳跃游戏 II】

2.3.1 解析

贪心 + 优先队列(堆)
我们可以模拟行进过程,使用 n 代表加油站总个数,idx 代表经过的加油站下标,使用 remain 代表当前有多少油(起始有 remain = startFuel),loc 代表走了多远,ans 代表答案(至少需要的加油次数)。只要 loc < target,代表还没到达(经过)目标位置,我们可以继续模拟行进过程。每次将 remain 累加到 loc 中,含义为使用完剩余的油量,可以去到的最远距离,同时将所在位置 stations[idx][0] <= loc 的加油站数量加入优先队列(大根堆,根据油量排倒序)中。再次检查是否满足 loc < target(下次循环),此时由于清空了剩余油量 remain,我们尝试从优先队列(大根堆)中取出过往油量最大的加油站并进行加油(同时对加油次数 ans 进行加一操作)。使用新的剩余油量 remain 重复上述过程,直到满足 loc >= target 或无油可加。容易证明该做法的正确性:同样是消耗一次加油次数,始终选择油量最大的加油站进行加油,可以确保不存在更优的结果。作者:宫水三叶
链接:https://leetcode.cn/problems/minimum-number-of-refueling-stops/solutions/1639184/by-ac_oier-q2mk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {public int minRefuelStops(int target, int startFuel, int[][] stations) {// 使用优先队列,承装所经过加油站的油PriorityQueue<Integer> q = new PriorityQueue<>((o1, o2) -> (o2 - o1));int ans = 0, len = stations.length;// 特判:if (len < 1) return startFuel < target ? -1 : 0;int fuel = startFuel;// 加进油箱的油(含使用过的)// 经过可以到达的所有的加油站,背上里面的油for (int i = 0; i < len; i ++) {while (fuel < stations[i][0]) {Integer add = q.poll();if (add == null) return -1;fuel += add;ans ++;}q.offer(stations[i][1]);}// 已经经过所有的加油站仍未到达,则用车油箱和后备箱里的所剩的fuel,以期到达while (fuel < target) {Integer add = q.poll();if (add == null) return -1;fuel += add;ans ++;}return ans;}/**题目思路:- 将路上的一个个加油站 视为 一桶桶的油,每次经过的时候,就把油带上放后备箱;- 当油不够的时候,取出后备箱所带的 最多的那桶油 加进油箱- 这样以来,如若油箱和后备箱的油加起来都不够,那么就到不了了*/// 方法二:需要后处理public int minRefuelStops2(int target, int startFuel, int[][] stations) {PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->b-a);int re=startFuel;int ans=0;int n=stations.length;int pos=0;int idx=0;while(pos<target){if(re==0){if(!q.isEmpty()&&++ans>0){re=q.poll();}else{return -1;}}pos+=re;re=0;while(idx<n&&pos>=stations[idx][0]){q.add(stations[idx++][1]);}}return ans;}
}

2.4 LC630. 课程表 III (同2.2的解题思想非常相似,反悔堆问题)

思路:如果能增大课程数量,就增大课程数量,如果不能增大课程数量,就在保持课程数量不变的前提下,尝试减少总时长,使得后续增大课程数量的概率增加; 推荐看这个解析:贪心还能反悔?

class Solution {public int scheduleCourse(int[][] courses) {Arrays.sort(courses, (a,b)->a[1]-b[1]);PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->b-a);int sum = 0;for (int[] c : courses) {int d = c[0], e = c[1];sum += d;q.add(d);if (sum > e) sum -= q.poll();}return q.size();}
}作者:宫水三叶
链接:https://leetcode.cn/problems/course-schedule-iii/solutions/1156693/gong-shui-san-xie-jing-dian-tan-xin-yun-ghii2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.5 LC45. 跳跃游戏 II(左最优问题),跟LC871. 最低加油次数 有点儿类似

class Solution {public int jump(int[] nums) {int n=nums.length;int curf=0;int ans=0;int ml=0;// 遍历时稍带计算左侧局部最优for(int i=0;i<n;i++){if(curf>=n-1){return ans;}ml=Math.max(ml,i+nums[i]);if(i==curf){ans++;curf=ml;}}return ans;}}

2.6 股票问题:121. 买卖股票的最佳时机(虽然使用堆的方法不是最优解,但是方便和其他题目对比得出最优解)

	// 使用堆维护最小值public int maxProfit(int[] prices) {int n=prices.length;//表示到第i天时,股票的历史最低点价格PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o1-o2));int max=0;for(int i=0;i<n;i++){pq.add(prices[i]);max=Math.max(max,prices[i]-pq.peek());}return max;}

2.7 LC2813. 子序列最大优雅度[反悔贪心]

3 “右最优” 类题目

3.1 LC670. 最大交换

搞懂两个问题,便可彻底理解本题的**(右最优)贪心核心**。

选择哪个数作为候选数与前面的数交换?——将最靠后的数定为候选数,若它之前出现了更大的数,则更新候选数为该数。
选择哪个数与候选数交换?——只有当候选数之前存在更小的数时,才需要交换这两数。若更靠前的位置出现小于候选数的数,则将它与候选数交换。

class Solution {public int maximumSwap(int num) {char[] cs = String.valueOf(num).toCharArray();int n = cs.length, max = n - 1;int[] maxIdx = new int[n]; // 记录每个数从当前位置往右的最大值索引for (int i = n - 1; i >= 0; i--) {if (cs[i] > cs[max]) max = i;maxIdx[i] = max;}for (int i = 0; i < n; i++) { // 从前往后找到第一个最大值不是自己本身的数if (cs[i] != cs[maxIdx[i]]) {char tmp = cs[i];cs[i] = cs[maxIdx[i]];cs[maxIdx[i]] = tmp;return Integer.parseInt(new String(cs));}}return num;}
}

4 同时涉及到左最优和右最优预处理的题目

4.1 LC42 接雨水(需要两边同时进行预处理)

class Solution {public int trap(int[] height) {int n = height.length;if (n == 0) {return 0;}int[] leftMax = new int[n];leftMax[0] = height[0];for (int i = 1; i < n; ++i) {leftMax[i] = Math.max(leftMax[i - 1], height[i]);}int[] rightMax = new int[n];rightMax[n - 1] = height[n - 1];for (int i = n - 2; i >= 0; --i) {rightMax[i] = Math.max(rightMax[i + 1], height[i]);}int ans = 0;for (int i = 0; i < n; ++i) {ans += Math.min(leftMax[i], rightMax[i]) - height[i];}return ans;}
}作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

仿真验证方法(3)——物理验证

目录 一、物理验证的分类 二、DRC 2.1 设计规则 2.2 规则示例 2.3 线宽违例 2.4 间距违例 2.5 交叠违例 三、金属覆盖图形密度检查 四、天线比率检查 4.1 起因 4.2 计算 4.3 改进 五、LVS检查 六、物理验证常用的EDA工具 七、总结 一、物理验证的分类 对于物理验…

专业课145+合肥工业大学833信号分析与处理考研经验合工大电子信息通信

今年专业课145也是考研科目中最满意的一门&#xff0c;其他基本相对平平&#xff0c;所以这里我总结一下自己的专业课合肥工业大学833信号分析与处理的复习经验。 我所用的教材是郑君里的《信号与系统》&#xff08;第三版&#xff09;和高西全、丁玉美的《数字信号处理》&…

java方法的定义和使用

方法 今日目标&#xff1a; 能够知道方法的好处 能够根据两个明确分析方法的参数和返回值 能够编写方法完成授课案例&#xff0c;并在主方法中完成方法的调用 能够知道方法重载及其特点 1&#xff1a;方法的定义和使用 1.1 方法概述 方法(method)&#xff1a;就是完成特…

Java常用的加密技术

项目结构&#xff1a; 总体代码&#xff1a; package VirtualUtils; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.sp…

深入浅出线程原理

Linux 中的线程本质 线程接口由 Native POSIX Thread Library 提供&#xff0c;即&#xff1a;NPTL 库函数 线程被称为轻量级进程 (Light Weight Process) 每一个线程在内核中都对应一个调度实体&#xff0c;拥有独立的结构体 (task_struct) 内核设计&#xff1a;一个进程对…

【python】matplotlib画图常用功能汇总

目录: 一、matplotlib画图风格二、matplotlib图像尺寸和保存分辨率三、matplotlib子图相关功能创建子图&#xff1a;绘制子图&#xff1a;设置子图属性&#xff1a;调整布局&#xff1a;示例代码&#xff1a; 四、matplotlib字体设置字体族和字体的区别字体选择和设置1. Matplo…

Kubernetes实战(十六)-k8s节点打标签

pod可以根据调度策略让pod调度到想要的节点上运行&#xff0c;或者不在某节点运行。 1 查看现有节点运行环境已有标签 $ kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS ops-master-1 Ready control-plane,ma…

亚马逊云科技 WAF 部署小指南(五):在客户端集成 Amazon WAF SDK 抵御 DDoS 攻击...

方案介绍 在 WAF 部署小指南&#xff08;一&#xff09;中&#xff0c;我们了解了 Amazon WAF 的原理&#xff0c;并通过创建 WEB ACL 和托管规则防护常见的攻击。也了解了通过创建自定义规则在 HTTP 请求到达应用之前判断是阻断还是允许该请求。在 Amazon WAF 自定义规则中&am…

代码随想录算法训练营29期Day17|LeetCode 110,257,404

文档讲解&#xff1a;代码随想录 110.平衡二叉树 题目链接&#xff1a;https://leetcode.cn/problems/balanced-binary-tree/description/ 思路&#xff1a; 本题要求我们判断二叉树每个节点 的左右两个子树的高度差的绝对值是否超过 1。我们很容易就能想到利用dfs去做&#…

水果音乐编曲软件 FL Studio v21.2.2.3914 中文免费版(附中文设置教程)

FL studio21中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简单有效&#xff0c;您的工作会变得…

【书生·浦语】大模型实战营——第四课作业

教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/main/xtuner/self.md 基础作业需要构建数据集&#xff0c;微调模型&#xff0c;让其明白自己的弟位&#xff08;OvO&#xff01;&#xff09; 微调环境准备 进入开发机后&#xff0c;先bash&#xff0c;再创…

Vue模板的理解和使用

Vue模板 Vue.js 的模板是一种声明性的语法&#xff0c;用于将数据渲染进 DOM&#xff08;文档对象模型&#xff09;。它们使开发者能够以直观的方式声明式地描述用户界面应该如何根据应用程序数据的变化动态显示。 Vue模板的主要特点包括&#xff1a; 数据绑定&#xff1a; …

列表解析与快速排序

排序是在对文本、数值等数据进行操作时常用的功能&#xff0c;本文介绍两种常用的排序方式&#xff0c;借此学习列表解析&#xff0c;并巩固递归算法。 1 选择排序 说到排序&#xff0c;以数值为例&#xff0c;肯定涉及到值大小的对比&#xff0c;选择排序即通过依次在子集中…

蓝桥杯 python 第二题 数列排序

这里给出一种解法 """ # 错的 n int(input()) dp[int(i) for i in input().split(" ")] dp.sort() print(" ".join(str(i) for i in dp)) """#这个是对的 num int(input())l list(map(int, input().split()))l.sort()pr…

AI文本生图模型Stable Diffusion部署教程

本文基于CentOS8进行Stable Diffusion开源框架部署. 1. DNS配置(但是今天出现了偶尔无法下载问题) 为了加速Github访问,我在本机配置如下 (sd) [rootshenjian stable-diffusion-webui]# cat /etc/hosts 127.0.0.1 shenjian localhost localhost.localdomain localhost4 loca…

SpringCloud openFeign 之 获取被调用服务名

SpringCloud openFeign 之 获取被调用服务名 一. 概述 低版本 feign 只能获取到被调用方法的信息。 只有高版本 feign 才支持获取到被调用服务的信息。 二. 代码实现 package com.zxguan.springcloud2.template.user;import com.zxguan.springcloud2.template.user.config…

修改权限控制(chmod命令、chown命令)

1.chmod命令 功能&#xff1a;修改文件、文件夹权限&#xff08;注意&#xff0c;只有文件、文件夹的所属用户或root用户可以修改&#xff09; 语法&#xff1a;chmod [-R] 权限 参数 权限&#xff0c;要设置的权限&#xff0c;比如755&#xff0c;表示&#xff1a;rwxr-xr-x…

Crow:蓝图路由2 Blueprint::register_blueprint

Crow:蓝图路由1 CROW_BP_ROUTE-CSDN博客 介绍了蓝图路由主要的一个作用就是将路由划分成蓝图路由根目录,然后再在蓝图路由创建子路由。 蓝图路由其实还可以在其下注册新的子蓝图路由,从而实现子目录的继续划分: crow::Blueprint bp("bp_prefix", "cstat&q…

【WPF.NET开发】WPF中的XAML资源

本文内容 使用 XAML 中的资源静态和动态资源静态资源动态资源样式、DataTemplate 和隐式键 资源是可以在应用中的不同位置重复使用的对象。 资源的示例包括画笔和样式。 本概述介绍如何使用 Extensible Application Markup Language (XAML) 中的资源。 你还可以使用代码创建和…