代码随想录刷题day32|K次反转后最大的数组和加油站分发糖果

文章目录

  • day34学习内容
  • 一、K次反转后最大的数组和
    • 1.1、思路
    • 1.2、代码-正确写法
      • 1.2.1、如何理解if (k % 2 == 1) ?
      • 1.2.2、原始nums数组=[2,-3,-1,5,-4],那么排序后数组等于什么?
  • 二、加油站
    • 2.1、思路
    • 2.2、正确写法1
      • 2.2.1、 如何理解上面这段代码
      • 2.2.2、如何理解index = (i + 1) % gas.length ; ?
  • 三、分发糖果
    • 3.1、思路
        • 第一阶段:从左向右,人话讲就是右边的小孩比左边大
        • 第二阶段:从右向左,人话讲就是左边的小孩比右边大
        • 计算总糖果数
    • 3.2、正确写法1
      • 3.2.1、为什么第二阶段,i要从length-2的位置开始
      • 3.2.2、为什么第二阶段,要从右往左遍历呢?
      • 3.2.3 、为什么要Math.max(candyVec[i], candyVec[i + 1] + 1);?
  • 总结
    • 1.感想
    • 2.思维导图


day34学习内容

day34主要内容

  • K次反转后最大的数组和
  • 加油站
  • 分发糖果

声明
本文思路和文字,引用自《代码随想录》

一、K次反转后最大的数组和

1005.原题链接

1.1、思路

一共经过俩次贪心的过程

  • 第一次:把数组按绝对值排序,然后把负数取反,因为这样取反后,累加和一定是更大的
  • 第二次:如果把所有负数取反后,K次没有用完,那就把数组最后一个元素取反剩余的次数
  • 经过以上2步,得到的数组和就是最大的。

1.2、代码-正确写法

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        // 先对数组按绝对值排序
        int[] sortedArray = IntStream.of(nums)
                .boxed()
                .sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
                .mapToInt(Integer::intValue)
                .toArray();

        // 第一次贪心,优先对负数取反
        for (int i = 0; i < sortedArray.length; i++) {
            if (sortedArray[i] < 0 && k > 0) {
                sortedArray[i] = -sortedArray[i];
                k--;
            }
        }

        // 所有负数都取反后,如果k次没有用完,就对数组末尾取反
        if (k % 2 == 1) {
            sortedArray[sortedArray.length - 1] = -sortedArray[sortedArray.length - 1];
        }

        return Arrays.stream(sortedArray).sum();
    }
}

1.2.1、如何理解if (k % 2 == 1) ?

在这段代码中,if (k % 2 == 1)这个条件用于判断变量k(表示还可以进行的翻转次数)是否为奇数。

在整数运算中,%是取余操作符,k % 2的结果就是k除以2后的余数。如果一个数除以2的余数是1,那么这个数一定是奇数;如果余数是0,则这个数是偶数。

  • k % 2 == 1时,意味着k是奇数。在这个问题的上下文中,如果经过前面的操作后k仍为奇数,说明你还有一次翻转机会未使用。由于前面的策略是优先将负数翻转成正数,此时数组中应该没有负数了(如果有负数,那么k应该会被用完),或者k的次数多于数组中负数的数量。因此,剩下的一次翻转操作可以应用于数组中任意一个数,但为了使总和尽可能大,这里选择翻转绝对值最小的数(由于数组已经按绝对值降序排列,最后一个元素的绝对值最小),这种情况下即数组的最后一个元素。

  • 如果k % 2 == 0(即k是偶数),则没有额外的翻转需要进行,因为两次相同的翻转会相互抵消,不会影响总和。

因此,这个条件判断是为了处理最后一个可能未使用的翻转机会,确保在所有翻转机会都用完后,能够得到最大的数组总和。

1.2.2、原始nums数组=[2,-3,-1,5,-4],那么排序后数组等于什么?

看代码,是按照绝对值排序的,所以排序后数组 = [5,-4,-3,2,-1]
第一次排序后数组的值


二、加油站

134.原题链接

2.1、思路

建议看代码随想录的文字版本。

  1. 初始化变量

    • curSum用于记录从当前加油站出发,到达下一个加油站后的净油量。
    • totalSum用于记录整个路线上的净油量总和。
    • index用于标记可能的出发加油站索引。
  2. 遍历加油站

    • 对于每个加油站,计算从该加油站出发到达下一个加油站后的净油量(即gas[i] - cost[i]),并累加到curSumtotalSum中。
    • 如果在到达某个加油站后curSum变为负数,意味着无法从当前的出发点到达这个加油站。因此,需要将出发点设为这个加油站的下一个站点(index = i + 1),并将curSum重置为0,因为我们从新的出发点开始计算。
  3. 判断是否可以绕圈一周

    • 遍历结束后,检查totalSum。如果totalSum为负数,说明整个路线的总油量不足以支撑整个旅程,因此无法完成环路,返回-1
    • 如果totalSum非负,这意味着总的油量足以支撑整个旅程。由于之前的循环已经确保了从任一点出发,只要curSum为负就更换出发点,因此最后index所标记的加油站即为可以作为起点绕圈一周的加油站索引。
  4. 总结
    这个算法的核心在于两个贪心策略的应用:

  • 局部最优解:通过在curSum变为负时立即更换出发点,保证了从每一个选定的出发点到当前点的过程中,油量都是足够的。
  • 全局最优解:通过确保totalSum非负来保证整个路线上的油量足以完成全程。

这种方法的优势在于只需遍历一次加油站数组,时间复杂度为O(n),且不需要额外的空间复杂度,非常高效。

2.2、正确写法1

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // 从当前加油站出发,油的净油量
        int curSum = 0;
        int totalSum = 0;
        int index = 0;
        for (int i = 0; i < gas.length; i++) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            // 当前加油站出发,当前的净油量为负数,
            // 说明从当前加油站到下一个加油站的油量不够,需从当前加油站的下一站出发
            if (curSum < 0) {
                index = i + 1;
                // 卡尔题解里面是index = (i + 1) % gas.length ; 
                curSum = 0;
            }
        }
        // 总的净油量为负数,那么不可能完成
        if (totalSum < 0) {
            return -1;
        }
        return index;
    }
}

2.2.1、 如何理解上面这段代码

  1. 初始化变量

    • curSum用于记录当前从某个加油站出发后的累积净汽油量(即已经加的油减去消耗的油)。
    • totalSum用于记录所有加油站的总净汽油量。
    • index用于记录可能的出发加油站编号。
  2. 遍历加油站
    循环遍历每一个加油站,计算从该加油站出发的curSum(当前净汽油量)和所有加油站的totalSum(总净汽油量)。对每个加油站,都做以下计算:

    • 将当前加油站的汽油量gas[i]与到下一站所需的汽油量cost[i]相减,然后加到curSumtotalSum上。
    • 如果在某一站curSum变为负数,说明从上一个index出发到这个加油站的途中油量不够,无法到达当前加油站。因此,需要将index更新为下一个加油站的编号(即i+1),并将curSum重置为0,以从新的加油站重新开始计算。
  3. 判断总净汽油量
    遍历完成后,用totalSum判断整个环路是否可以绕一圈。如果totalSum小于0,说明总的汽油量不足以支撑整个旅程,返回-1。如果totalSum大于等于0,说明至少存在一条路径可以让车辆绕圈一周,这时返回记录的index,即可能的出发加油站编号。

2.2.2、如何理解index = (i + 1) % gas.length ; ?

  1. 确定下一个出发点
    当当前累积的汽油量curSum在某个点变为负值时,意味着从最近一次尝试的出发点到当前加油站的途中,汽油是不足以支持行驶到当前加油站的。因此,当前加油站不能作为出发点,需要尝试下一个加油站作为新的起点。这里的i + 1就是为了将起点设置为当前加油站的下一个加油站。

  2. 循环遍历数组
    因为问题本质上是在一个圆圈上的旅行,所以当索引到达数组的末尾时,它需要回到数组的开始,形成一个闭环。% gas.length确保了index值在超过加油站数量(也就是gas数组的长度)时能够循环回到数组的开头。例如,如果gas.length是5,当i等于4时,i + 1等于5,此时5 % 5的结果是0,即数组的第一个元素,符合循环的逻辑。

综上所述,index = (i + 1) % gas.length;这行代码的意义在于,当发现从某个加油站出发无法继续行驶时,尝试将下一个加油站作为起点,并保证这个过程可以在加油站形成的闭环上顺利进行,即使尝试的新起点是数组的第一个元素。


三、分发糖果

135.原题链接

3.1、思路

  • 举个例子看懂题目吧。

假设我们有4个孩子,他们的评分分别是:ratings = [1, 2, 2, 1]。我们的目标是给每个孩子分发糖果,条件是评分更高的孩子必须比他相邻的孩子得到更多的糖果。

第一阶段:从左向右,人话讲就是右边的小孩比左边大
  1. 初始化:每个孩子至少得到1颗糖果。所以开始时,candyVec = [1, 1, 1, 1]
  2. 从左向右遍历
    • 第一个孩子的左边没有孩子,所以他得到1颗糖果。
    • 第二个孩子(评分2)比第一个孩子(评分1)评分高,所以他得到的糖果数为第一个孩子的糖果数+1,即2颗糖果。现在candyVec = [1, 2, 1, 1]
    • 第三个孩子的评分等于第二个孩子,所以他仍然只得到1颗糖果。candyVec不变。
    • 第四个孩子的评分低于第三个孩子,所以他也只得到1颗糖果。candyVec不变。

这时,candyVec = [1, 2, 1, 1]

第二阶段:从右向左,人话讲就是左边的小孩比右边大
  1. 从右向左遍历
    • 第三个孩子比第四个孩子评分高,但按照从左向右的结果,他们的糖果数是一样的。为了满足条件,我们需要给第三个孩子更多的糖果。所以,我们将第三个孩子的糖果数更新为第四个孩子的糖果数+1,即2颗糖果。现在candyVec = [1, 2, 2, 1]
    • 第二个孩子和第一个孩子相比,不需要改变,因为他们的评分和糖果数已经符合规则。

这时,candyVec = [1, 2, 2, 1]

计算总糖果数

遍历candyVec数组,计算总糖果数:1 + 2 + 2 + 1 = 6

所以,最终答案是6颗糖果。

综上,总体思路就是先确定一边,再确定另一边。

3.2、正确写法1

class Solution {
    public int candy(int[] ratings) {
        int len = ratings.length;
        int[] candyVec = new int[len];
        candyVec[0] = 1;
        // 第一阶段,从左往后
        for (int i = 1; i < len; i++) {
            candyVec[i] = (ratings[i] > ratings[i - 1]) ? candyVec[i - 1] + 1 : 1;
        }

        // 第二阶段,从右往左
        for (int i = len - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1);
            }
        }

        // 满足条件,求和
        int ans = 0;
        for (int num : candyVec) {
            ans += num;
        }
        return ans;
    }
}

3.2.1、为什么第二阶段,i要从length-2的位置开始

因为length-1正好是最后一个元素,最后一个元素没有右孩子,不需要和任何孩子比较,所以是length-2、

3.2.2、为什么第二阶段,要从右往左遍历呢?

看图
在这里插入图片描述

3.2.3 、为什么要Math.max(candyVec[i], candyVec[i + 1] + 1);?

为什么要用max?

1、因为ratings[i] > ratings[i + 1],说明左边比右边的孩子得分多,所以左边这个孩子的candy[i] = candy[i]+1,
2、又因为第一步,按照从左到右遍历顺序,已经得到了candy[i]
3、到此时,经过【1】,说明已经满足了左边比右边大。又因为按照题意,当前孩子就要比左边(前一个)孩子的糖果多,又要比右边的(后面的)孩子糖果多,此时还剩【比左边(前一个)孩子的糖果多】没有满足,
4、如果左边的比右边大,此时,candy[i] = candy[i+1]+1;
5、所以,candy[i]要在candy[i]和candy[i+1]+1之间取最大数。也就是取第一种情况和第二种情况的最大值。

总结

1.感想

  • 第三题分发糖果好难

2.思维导图

本文思路引用自代码随想录,感谢代码随想录作者。

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

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

相关文章

数据可视化-ECharts Html项目实战(7)

在之前的文章中&#xff0c;我们学习了如何设置漏斗图、仪表盘。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢 数据可视化-ECharts Html项目实战&#xff08;6…

JavaScript 学习日记(1)---初识JavaScript

初识JavaScript 文章目录 初识JavaScript一、JavaScript 是什么?二、java 和JavaScript 的关系三、JavaScript 的组成四、JS的基本输入输出 ---> 单行注释五、js变量基本概念六、js基本数据类型七、js转义字符八、js类型转换九、运算符 END! 一、JavaScript 是什么? 我们…

FDGaussian:又快又好的三维重建方案 | Gaussian Splatting和扩散模型超强联合

项目地址&#xff1a;https://qjfeng.net/FDGaussian/ 文章链接&#xff1a;https://arxiv.org/pdf/2403.10242 本文介绍了一种名为FDGaussian的新型两阶段框架&#xff0c;用于单张图像的三维重建。最近的方法通常利用预先训练好的二维扩散模型从输入图像生成可能的新视图&…

DARTS-: ROBUSTLY STEPPING OUT OF PERFORMANCE COLLAPSE WITHOUT INDICATORS

DARTS-&#xff1a;增加辅助跳跃连接&#xff0c;鲁棒走出搜索性能崩溃 论文链接&#xff1a;https://arxiv.org/abs/2009.01027 项目链接&#xff1a;GitHub - Meituan-AutoML/DARTS-: Code for “DARTS-: Robustly Stepping out of Performance Collapse Without Indicators…

RAG笔记:常见问题以及解决方法

1 内容缺失 知识库中缺少必要的上下文信息。当知识库没有包含正确答案时&#xff0c;RAG 系统可能会给出一个貌似合理但实际上错误的回答&#xff0c;而不是明确表示它不知道答案。 1.1 解决方法 1.1.1 设置阈值 在回答问题前先设定一个质量标准。如果召回内容达不到标准或…

大数据Hadoop生态圈体系视频课程

课程介绍 熟悉大数据概念&#xff0c;明确大数据职位都有哪些&#xff1b;熟悉Hadoop生态系统都有哪些组件&#xff1b;学习Hadoop生态环境架构&#xff0c;了解分布式集群优势&#xff1b;动手操作Hbase的例子&#xff0c;成功部署伪分布式集群&#xff1b;动手Hadoop安装和配…

真假“长文本”,国产大模型混战

文&#xff5c;郝 鑫 Kimi有多火爆&#xff1f;凭一己之力搅乱A股和大模型圈。 Kimi概念股连日引爆资本市场&#xff0c;多个概念股随之涨停。在一片看好的态势中&#xff0c;谁都想来沾个边&#xff0c;据光锥智能不完全统计&#xff0c;目前&#xff0c;至少有包括读客…

(二)BSQ,BIL,BIP存储格式的相互转换算法

环境&#xff1a;Windows10专业版 IDEA2021.2.3 jdk11.0.1 GDAL(release-1928-x64-gdal-3-5-2-mapserver-8-0-0) 系列文章&#xff1a; &#xff08;一&#xff09;PythonGDAL实现BSQ&#xff0c;BIP&#xff0c;BIL格式的相互转换 &#xff08;二&#xff09;BSQ,BIL,BIP存…

【中间件】docker数据卷

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;中间件 ⛺️稳中求进&#xff0c;晒太阳 1.数据卷&#xff08;容器数据管理&#xff09; 修改nginx的html页面时&#xff0c;需要进入nginx内部。并且因为内部没有编辑器&#xff0c;修改…

手把手教集成环信新版UIKit组件,快速构建Android应用

前言 环信新版UIKit已重磅发布&#xff01;目前包含单群聊UIKit、聊天室ChatroomUIKit&#xff0c;本文详细讲解Android端单群聊UIKit的集成教程。 环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界…

网络编程(Internet)

网络编程三要素 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;可以进行数据传输。 IP地址 要想让网络中的计算机能够互相通信&#xff0c;必须为每台计算机指定一个标识号&#xff0c;通过这个标识号来指定要接收数据的计算机和识别发送的计算机&#xf…

【超图 SuperMap3D】【基础API使用示例】54、超图SuperMap3D -鼠标左键拖拽绘制圆

前言 引擎下载地址&#xff1a;[添加链接描述](http://support.supermap.com.cn/DownloadCenter/DownloadPage.aspx?id2524) 通过左键按下拖拽的方式在地图上进行贴地的圆绘制 完整代码拷贝直接本地运行即可查看效果效果 核心代码 // 绘制圆形 function startDrawCircleHand…

探究橡胶手套与乳胶手套的区别及选择指南

在探寻手部防护的世界里&#xff0c;橡胶手套与乳胶手套各自闪耀着独特的光芒。然而&#xff0c;对于许多人来说&#xff0c;这两者之间的细微差异仍然笼罩在一层迷雾之中。本文将如同一盏明灯&#xff0c;照亮这片迷雾&#xff0c;为您提供一份详尽的选择指南。 首先&#xff…

高性价比 惠海MOS管选型推荐 30V60V100V150V NMOS管和PMOS管

mos管选型建议&#xff1a; 需要抗电流冲击的&#xff0c;普通逻辑开关或者工作频率很低比如50kHZ以内的&#xff0c;电流非常大的情况比如实际工作6A以上的话一般建议选择平面mos管。6A以内的应用&#xff0c;大电流沟槽型mos管即可。 需要高频开关&#xff0c;比如500kHZ以…

2024/3/23打卡数组分割(第14届蓝桥杯)——二项式定理,快速幂

目录 题目 思路 代码 题目 思路 分析该题&#xff0c;要将集合 划分成两个子集 &#xff0c;且两个子集的和都是偶数。 可知&#xff1a;偶数 偶数 偶数&#xff1b;偶数 奇数 奇数&#xff1b;奇数 奇数 偶数&#xff1b; 分析可得&#xff1a;如果该集合的和为奇…

AtCoder Regular Contest 133 B - Dividing Subsequence 复杂版LIS最长上升子序列

B - Dividing Subsequence 题意&#xff1a; 数组a和数组b&#xff0c;找出最长公共子序列&#xff0c;使得每个b[ i ] 都是 a[ i ]的倍数。 思路&#xff1a; AtCoder Regular Contest 133 B(最长上升子序列) C(思维) - 知乎 这种题应把问题转化&#xff0c;把 要找的 和 …

父类子类构造方法调用示例

父类写无参构造&#xff0c;子类不写构造&#xff0c;实例化子类&#xff0c;会同时调用父类构造方法 public class Father {private String name;private int age;public Father() {System.out.println("父类无参构造");}} public class Son extends Father {priva…

prometheus监控oracle

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

element-ui实现各种证件照上传预览下载组件封装,图片上传回显及长宽自定义功能单个图片上传功能附带源码

element-ui实现证件照上传预览下载组件封装 效果&#xff1a; 参数说明 我只写了两个参数&#xff0c;后续有需求再对其组件进行丰富~ 参数说明fileListProp用来存储上传后后端返回的图片UR了uploadUrl图片上传返回的URL后端接口地址widthProp图片上传框的宽度heightProp图片…

人脸聚类原理和算法解释

人脸聚类是指将大量人脸图像根据它们的相似性分组到不同的群集中的过程。人脸聚类通常利用人脸的特征向量表示来度量人脸之间的相似性&#xff0c;并将相似的人脸图像聚集在一起。 以下是人脸聚类的一般原理&#xff1a; 人脸特征提取&#xff1a;对每张人脸图像提取特征向量。…