【代码随想录算法训练营第三十五天】 | 1005.K次取反后最大化的数组和 134.加油站 135.分发糖果

贪心章节的题目,做不出来看题解的时候,千万别有 “为什么这都没想到” 的感觉,想不出来是正常的,转变心态 “妙啊,又学到了新的思路” ,这样能避免消极的心态对做题效率的影响。

134. 加油站

按卡哥的思路:

首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。

每个加油站的剩余量rest[i]为gas[i] - cost[i]。

i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。

这种其实算是暴力解法的优化,相当于利用了前面的信息,当 sum 变成负数时,确认了 [ 0 ,i ] 的点都无法作为起点。

为什么一旦[0,i] 区间和为负数,起始位置就可以是i+1呢,i+1后面就不会出现更大的负数?

如果出现更大的负数,就是更新i,那么起始位置又变成新的i+1了。那有没有可能 [0,i] 区间 选某一个作为起点,累加到 i这里 curSum是不会小于零?

证明采用反证法,用卡哥的图来解释

如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。

区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。

class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int curSum = 0;int totalSum = 0;int start = 0;for (int i = 0; i < gas.size(); i++) {curSum += gas[i] - cost[i];totalSum += gas[i] - cost[i];if (curSum < 0) {   // 当前累加rest[i]和 curSum一旦小于0start = i + 1;  // 起始位置更新为i+1curSum = 0;     // curSum从0开始}}if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了return start;}
};

本题困扰我很久的地方在于另一种解法,我最开始将 gas[i] - cost[i] 的值做成数组,再利用前缀和的技巧,直观地想到,前缀和最低点的后一个点是最佳起点,以它为起点的后半段一定是能走通的,再保证全程有盈余,就可以走完全程。亏空最严重的一个点必须放在最后一步走,等着前面剩余的救助。

这种思路的想法是比较直觉的,问题在于这个加油站是环形的,怎么证明从不同的起点出发,无论能不能走完全程,全局最低点的位置是不变的。

这个结论的前提条件有点多,但题目都满足:

1、环形的数组。

2、起点可以变化,但数组中的元素及其顺序是固定的。换句话说,我们在分析累积和时并没有改变任何元素的值,只是改变了它们被累加的起始点和顺序。

3、数组的总和要大于0,即题目要求的有解,且必须是唯一解,多解的话这个解法失效,需要添加额外的判定条件。

4、环形数组中的元素是连续分布的,并且在整个分析过程中保持不变。

5、累积和计算方法是线性的,即从选定的起点开始,依次加上下一个元素,直到数组结束,然后继续从数组的开始处累加,直到返回到起点之前的元素。

满足这些条件才能保证,接下来详细说明。

如下图所示,从 0 索引开始的 curSum 的值的变化可以用折线图表示。

满足上述前提的话,在改变起点位置时,如以下标 1 作为起点,我们重新绘制这个折线图,我们能发现,这个图的变化规律—— 起点从 0 变为 1,则将 1 之前的折线拼接到这个图的最右端,这个变化的原因是因为数组是环形的,并且每个位置的值没有改变,所以值的相对大小关系没有改变,只是访问的顺序改变了,导致了折线图的变化。

同时,我们还能发现折线图除了刚刚的把起点左边的折线接在了之前的末尾,折线图也在上下的平移,因为累计值变化了,但除了拼接外,图形的大体形状是没有变化的。

然后我们接着改变起点,我们能发现还是这个变化规律,同时,无论从哪个起点开始,折线图的最低点永远是不变的。

在环形结构中,最低点的不变性基于一个事实:所有可能的前缀和最低值是从数组中某个特定序列的累积得到的,这个序列不会因为起点的改变而改变其累积和的值。虽然累积和的起点改变了,但你依然是在处理同样的一组数值,只是顺序变了。因此,数组中存在的最小和最大累积和出现的位置不会因为起点的改变而改变,它们只是在计算过程中出现的时间变了。每次你重新开始累积时,你都是在重新排列这些相同的值。最低点是由这些值的固定组合决定的,而不是由起点决定的。

这是通过观察折线图得出的结论,并不是严谨的数学证明。

解释完这个结论后,通过折线图来解释这个过程的话,我们在做的就是寻找在遍历所有点时, curSum 都不会小于0,如果存在这样的折线图,那么就找到了唯一解。

public int canCompleteCircuit(int[] gas, int[] cost) {int len = gas.length;int spare = 0;int minSpare = Integer.MAX_VALUE;int minIndex = 0;for (int i = 0; i < len; i++) {spare += gas[i] - cost[i];if (spare < minSpare) {minSpare = spare;minIndex = i;}}return spare < 0 ? -1 : (minIndex + 1) % len;
}

这两种思路其实代码都是差不多的,几乎没有不同,只是从两个角度得出的相同的结论。

135. 分发糖果

class Solution {
public:int candy(vector<int>& ratings) {vector<int> candyVec(ratings.size(), 1);// 从前向后for (int i = 1; i < ratings.size(); i++) {if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;}// 从后向前for (int i = ratings.size() - 2; i >= 0; i--) {if (ratings[i] > ratings[i + 1] ) {candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);}}// 统计结果int result = 0;for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];return result;}
};

这题学习到了前后两次遍历的解法。

这道题目一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼

先确定右边评分大于左边的情况(也就是从前向后遍历)

再确定左孩子大于右孩子的情况(从后向前遍历)

遍历顺序这里有同学可能会有疑问,为什么不能从前向后遍历呢?

因为 rating[5]与rating[4]的比较 要利用上 rating[5]与rating[6]的比较结果,所以 要从后向前遍历。

如果从前向后遍历,rating[5]与rating[4]的比较 就不能用上 rating[5]与rating[6]的比较结果了 。

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

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

相关文章

redis 06 集群

1.节点&#xff0c;这里是把节点加到集群中的操作&#xff0c;跟主从结构不同 这里是在服务端使用命令&#xff1a; 例子&#xff1a; 2.启动节点 节点服务器 首先&#xff0c;先是服务器节点自身有一个属性来判断是不是可以使用节点功能 一般加入集群中的节点还是用r…

GB/T 42740-2023 轨道交通用电线电缆检测

近几年随着我国经济快速的发展&#xff0c;电气火灾呈现高发趋势&#xff0c;鉴于电线电缆火灾的危险性&#xff0c;国家制定了阻燃&#xff0c;耐火电线电缆的标准&#xff0c;为企业&#xff0c;建设方&#xff0c;施工方等的生产&#xff0c;选材提供了指引。 GB/T 42740-2…

`DiffUtil` 是 Android 开发中用于高效处理列表数据变化的一个工具类。

DiffUtil 是 Android 开发中用于高效处理列表数据变化的一个工具类。它通过计算新旧数据列表的差异&#xff0c;并生成一个差异结果来仅更新那些内容发生变化的视图项&#xff0c;从而避免不必要的全量刷新操作。 为什么使用 DiffUtil 在开发 Android 应用时&#xff0c;经常…

微服务迁移、重构最佳经验

1. 了解现有的单体应用: - 应用架构和技术栈 要了解现有的应用架构和技术栈&#xff0c;可以采取以下几个步骤&#xff1a; 1. 了解应用的背景和目标&#xff1a;首先要了解应用的背景和目标&#xff0c;包括应用所属的行业、应用的类型&#xff08;例如Web应用、移动应用等…

【Spine学习06】之IK约束绑定,制作人物待机动画,图表塞贝尔曲线优化动作

引入IK约束的概念&#xff1a; 约束目标父级 被约束骨骼子集 这样理解更好&#xff0c;约束目标可以控制被约束的两个骨骼运作 IK约束绑定过程中呢&#xff0c;如果直接绑定最下面的脚掌骨骼会发生偏移&#xff0c;所以在开始处理IK之前&#xff0c;需要先设置一个ROOT结点下的…

创新入门|生成式AI创新赋能优势解析,获取生成式AI知识的10大方法

生成式AI技术对员工和企业影响深远。对于员工而言&#xff0c;生成式AI能够提升工作效率&#xff0c;简化重复性任务&#xff0c;并为创意和决策提供支持。对于企业而言&#xff0c;生成式AI在产品创新、市场营销、客户服务和运营优化等方面发挥重要作用&#xff0c;帮助预测市…

OpenHarmony napi 编译 .so 并打包成 .har

一、前言 最近在搞公司标准产品适配OpenHarmony 平台&#xff0c; 按照行业上的常用方法&#xff0c;在Android 是将底层代码用c 封装成 xxx.so &#xff0c;然后将其他一部分打包成 xxx.jar。 因此&#xff0c;在OpenHarmony 平台也是打算按照这个模式。正所谓&#xff0c;好…

共享会员,年赚30万,拆解背后隐藏的5套搞钱思路!

昨天一位朋友加我&#xff0c;咨询咱们知识付费资源站&#xff0c;想学习知识付费。他问我&#xff0c;是否可以在里面搜索到学生学习的软件会员&#xff0c;公司用的软件文档&#xff0c;还有一些运营工具。 在咱们资源站&#xff0c;虽然到现在为止已经收录了13000资源&…

JavaScript之函数

函数 使用 声明语法&#xff1a; function 函数名() {函数体 }命名规范&#xff1a; 小驼峰命名法前缀用动词 前缀词&#xff1a; 调用 函数名()函数传参 为了提高函数的灵活性 声明语法&#xff1a; function 函数名(参数列表) {函数体 }调用 函数名(参数)在函数声…

供应链初学者手册——第八部分:供应链战略与领导力

供应链初学者手册 文章目录 供应链初学者手册第八部分&#xff1a;供应链战略与领导力16. 供应链战略规划16.1 战略制定过程16.2 供应链战略的实施 17. 供应链领导力17.1 领导力在供应链管理中的作用17.2 供应链团队建设与管理 总结 第八部分&#xff1a;供应链战略与领导力 1…

Python版本管理器-Miniconda

随着Python的版本更新&#xff0c;我们在开发Python软件的时候&#xff0c;对Python的版本选择越来越重要&#xff0c;但同时又要兼容已经开发好了的Python软件&#xff0c;因此选择一款合适的Python版本管理器对提高开发效率也越来越重要&#xff0c;今天就推荐一款Python的版…

C# list 成员对象是int型存在堆区还是栈区

在C#中&#xff0c;List<int>中的元素是直接存储在堆上的。这是因为List<T>是一个引用类型&#xff0c;当你创建一个List<int>实例时&#xff0c;它的容器本身&#xff08;即列表的结构&#xff09;存储在栈上&#xff0c;但是其元素&#xff08;这里是int值…

深入理解指针(二)

目录 1. 数组名的理解 2. 使用指针访问数组 3. ⼀维数组传参的本质 4. 冒泡排序 5. 二级指针 6. 指针数组 7. 指针数组模拟二维数组 1. 数组名的理解 有下面一段代码: #include <stdio.h> int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p &arr[…

2024年6月14日 十二生肖 今日运势

小运播报&#xff1a;2024年6月14日&#xff0c;星期五&#xff0c;农历五月初九 &#xff08;甲辰年庚午月己酉日&#xff09;&#xff0c;法定工作日。今天世界献血日&#xff0c;捐献新鲜血液&#xff0c;挽救更多生命&#xff0c;每位献血者都是英雄&#xff01; 红榜生肖…

美创科技入选“2024网络安全提供商创新排行榜”

近日&#xff0c;DBC德本咨询公布了“2024网络安全提供商创新排行榜”&#xff0c;美创科技凭借近20年的数据安全创新耕耘&#xff0c;荣誉上榜。 此次&#xff0c;与360、华为、腾讯等互联网、网络安全头部厂商并肩上榜&#xff0c;是行业对美创的再次认可。 数据安全的发展离…

什么是基于风险的漏洞管理RBVM,及其优势

文章目录 一、什么是漏洞管理二、什么是基于风险的漏洞管理RBVM三、RBVM的基本流程四、RBVM的特点和优势 一、什么是漏洞管理 安全漏洞是网络或网络资产的结构、功能或实现中的任何缺陷或弱点&#xff0c;黑客可以利用这些缺陷或弱点发起网络攻击&#xff0c;获得对系统或数据…

Redis保证数据⼀致、缓存穿透、缓存雪崩和数据热点

13- Redis和Mysql如何保证数据⼀致? 先更新Mysql&#xff0c;再更新Redis&#xff0c;如果更新Redis失败&#xff0c;可能仍然不⼀致 先删除Redis缓存数据&#xff0c;再更新Mysql&#xff0c;再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题&#xff0c;但…

【log4】log4cplus:使用详解(一)

1、源码下载 源码下载地址:https://sourceforge.net/projects/log4cplus/files/log4cplus-stable/ 最新稳定版本为2.1.1(2023-11-17) github中有最新的源码:https://github.com/log4cplus/log4cplus 2、源码编译 1)解压后,进入源码目录中,执行配置命令: ./confi…

OpenZeppelin Ownable合约 怎么使用

文章目录 智能合约的访问控制Ownable合约使用方法 智能合约的访问控制 熟悉OpenZeppelin的智能合约库的开发者都知道这个库已经提供了根据访问等级进行访问限制的选项&#xff0c;其中最常见的就是Ownable合约管理的onlyOwner模式&#xff0c;另一个是OpenZeppelin的Roles库&a…

闪烁圆点加载动画

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>闪烁圆点加载动画</title><style type="text/css">body {background: #ECF0F1;display: flex;justify-content: center;al…