前缀和+单调双队列+贪心:LeetCode2945:找到最大非递减数组的长度

本文涉及知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
单调双队列 贪心

题目

给你一个下标从 0 开始的整数数组 nums 。
你可以执行任意次操作。每次操作中,你需要选择一个 子数组 ,并将这个子数组用它所包含元素的 和 替换。比方说,给定数组是 [1,3,5,6] ,你可以选择子数组 [3,5] ,用子数组的和 8 替换掉子数组,然后数组会变为 [1,8,6] 。
请你返回执行任意次操作以后,可以得到的 最长非递减 数组的长度。
子数组 指的是一个数组中一段连续 非空 的元素序列。
示例 1:
输入:nums = [5,2,2]
输出:1
解释:这个长度为 3 的数组不是非递减的。
我们有 2 种方案使数组长度为 2 。
第一种,选择子数组 [2,2] ,对数组执行操作后得到 [5,4] 。
第二种,选择子数组 [5,2] ,对数组执行操作后得到 [7,2] 。
这两种方案中,数组最后都不是 非递减 的,所以不是可行的答案。
如果我们选择子数组 [5,2,2] ,并将它替换为 [9] ,数组变成非递减的。
所以答案为 1 。
示例 2:
输入:nums = [1,2,3,4]
输出:4
解释:数组已经是非递减的。所以答案为 4 。
示例 3:
输入:nums = [4,3,2,6]
输出:3
解释:将 [3,2] 替换为 [5] ,得到数组 [4,5,6] ,它是非递减的。
最大可能的答案为 3 。
参数范围
1 <= nums.length <= 105
1 <= nums[i] <= 105

枚举最后一个子数组nums(j,i]

假定结果向量为vRet
nums[0,j] 需要记录两个子状态:最有一个子数组的和,vRet的长度(操作前的子数组数量)。枚举这些状态时间复杂度O(n),枚举i时间复杂度O(n),枚举j时间复杂度O(n)。故总时间复杂度o(n3)。合并nums[0,j]和nums(j,i]有两种操作:
操作一:nums(j,i]全部合并到vRet[j]的最后一个元素。无前提条件。
操作二:nums(j,i]成为新元素。前天条件nums(j,i]大于vRet[j]的最后一个元素。

贪心:不存在len > len2且v1 > v2

令nums[0,i)合法分成n段,最后一段的值为fin,即分成一段,最后的值为fi1,分成两段,最后的值为fi2。
令nums[0,j)…fjn。

证明:对于任意数组(子数组),fxn 一定 大与等于fxn+1。x是j,j等…
n==1: fx1 为整个数组的和,fx2为数组第二部分的和。显然成立。
n >1 用反证法:
假定可以合法分成n段,分别为{a1,a2…an}
假定可以合法分成n+1段:分别为(b1,b2…bn,bn+1}。bn+1可能有多个值,取最小值
假定an < bn+1,则{a1,a2,…an-1} 和大于{b1…bn}的和 => 假定前者包括nums[0,i)后者包括nums[0,j) => i >j
则{b1,b2…bn+nums[j,i)}是nums[0,i)的一个合法分成n段,{a1…an-1} 是nums[0,i)一个合法n-1段。

bn+nums[j,i)就是fin, an-1,就是fn-1 fxn-1 >= fnx =>an-1 > bn+nums[j,i)
因为an >= an-1 => {b1,b2…bn+nums[j,i),an} 是一个nums的n+1段划分。
结合假设:an <bn+1 和 bn+1是最小值矛盾

优化后时间复杂度O(n)

如果nums[0,i)存在长度len,则nums[0,i]一定存在长度为len的结果:将nums[i]追加到最后。
判断nums[0,i]能否则在nums[0,j] 增加一个元素(nums(j,i]的和),需要判断
vPreSum[i+1] - vPreSum[j+1] >= Last[j]
即vPreSump[i+1] >= Last[j]+vPreSum[j+1]
Last[j] 是最后元素的值
Last[j]+vPreSum[j+1] 可以合并一个变量llPre。已知状态只需要保留最大长度,长度相同保留llPre最小。
此解法错误,还在摸索中。

class Solution {
public:int findMaximumLength(vector<int>& nums) {m_c = nums.size();auto vPreSum = CreatePreSum(nums);int len = 0;long long llPre = 0;int preIndex = -1;for (int i = 0; i < m_c; i++){if (vPreSum[i + 1] >= llPre){len++;llPre = vPreSum[i+1] + (vPreSum[i + 1] - vPreSum[preIndex + 1]);preIndex = i;}else{const long long curAdd  = vPreSum[i+1] - vPreSum[preIndex+1] + (vPreSum[i + 1] - vPreSum[preIndex + 1]);if (curAdd < 0){llPre += curAdd;preIndex = i;}}}return len;}int m_c;
};

正确解法

下标从小到大遍历nums[i],queIndexs记录[0,i),淘汰以下:
一,j1 < j2,也就(j1,i]的和大于(j2,i]。也就是j1的最后一个数大于j2的最后一个数。llPre1 大于llPre2,也就llPre1更难匹配,淘汰j1。淘汰后:下标升序 llPre 降序。
二,j找到第一个i后,淘汰。假定j的长度为len,i1 < i2。i1可以选择{… (j,i1],(i1,i2]} 和{…{j,i2]},而i2只能选择后者,所以i1不劣与i2。

注意:如果无法长度+1,则vLast[i] = vLast[i - 1] + nums[i]

队首元素的长度和队尾元素的长度相差不会超过1

双向队列中的下标升序,也就是长度升序。
假定新入队的长度是len,则被淘汰的队首长度为len-1。由于是升序,所以队中元素的长度都大于等于len-1,小于等于len

template<class T = long long >
vector<T> CreatePreSum(const vector<int>& nums)
{vector<T> preSum;preSum.push_back(0);for (int i = 0; i < nums.size(); i++){preSum.push_back (preSum[i]+ nums[i]);}return preSum;
}class Solution {
public:int findMaximumLength(vector<int>& nums) {m_c = nums.size();auto vPreSum = CreatePreSum(nums);vector<int> vRet(m_c,1),vLast(m_c,nums.front());std::deque<int> queIndexs;queIndexs.emplace_back(0);auto Pre = [&](const int index) {return vPreSum[index + 1] + vLast[index];  };for (int i = 1; i < m_c; i++){vRet[i] = vRet[i - 1];vLast[i] = vLast[i - 1] + nums[i];while (queIndexs.size() && (vPreSum[i + 1] >= Pre(queIndexs.front()))){vRet[i] = vRet[queIndexs.front()]+1;vLast[i] = vPreSum[i + 1] - vPreSum[queIndexs.front() + 1];queIndexs.pop_front();}while (queIndexs.size() && (Pre(queIndexs.back()) >= Pre(i))){queIndexs.pop_back();}queIndexs.emplace_back(i);}return vRet.back();}int m_c;
};

错误解法

class Solution {
public:
int findMaximumLength(vector& nums) {
stack sta;
int i = 0;
while (i < nums.size())
{
long long cur = 0;
while (sta.size() && (i < nums.size()) && (sta.top() > cur + nums[i]))
{
cur += nums[i++];
}
if (i >= nums.size())
{
break;//理论上需要栈顶的值,由于我需要的是数量,所以可以不修改
}
sta.emplace(cur+ nums[i++]);
}
return sta.size();
}
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法C++ 实现。

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

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

相关文章

python脚本 ssh工具 ssh上传文档 选择文档并上传到ssh服务器

此文分享一个python脚本,用于快速的定位、选择文档,并将其上传到指定的ssh服务器。 效果演示 🔥完整演示效果 👇第一步,显然,我们需要选择功能 👇第二步,我们需要定位并选择需要上传的文档 👇第三步,确认我们需要上传文档的ssh服务器 👇第四步,定位、选择…

Qt的简单游戏实现提供完整代码

文章目录 1 项目简介2 项目基本配置2.1 创建项目2.2 添加资源 3 主场景3.1 设置游戏主场景配置3.2 设置背景图片3.3 创建开始按钮3.4 开始按钮跳跃特效实现3.5 创建选择关卡场景3.6 点击开始按钮进入选择关卡场景 4 选择关卡场景4.1场景基本设置4.2 背景设置4.3 创建返回按钮4.…

模式识别与机器学习(十一):Bagging

1.原理 Bagging [Breiman, 1996a] 是井行式集成学习方法最著名的代表.从名字即可看出&#xff0c;它直接基于自助采样法(bootstrap sampling)。给定包含m 个样本的数据集&#xff0c;我们先随机取出一个样本放入采样集中&#xff0c;再把该样本放回初始数据集&#xff0c;使得…

【JAVA】分布式链路追踪技术概论

目录 1.概述 2.基于日志的实现 2.1.实现思想 2.2.sleuth 2.2.可视化 3.基于agent的实现 4.联系作者 1.概述 当采用分布式架构后&#xff0c;一次请求会在多个服务之间流转&#xff0c;组成单次调用链的服务往往都分散在不同的服务器上。这就会带来一个问题&#xff1a;…

网络基础知识制作网线了解、集线器、交换机与路由器

目录 一、网线的制作 1.1、材料 1.2、网线的标准类别 二、集线器、交换机介绍 2.1、概念&#xff1a; 2.2、OSI七层模型 2.3、TCP/IP四层 三、路由器的配置 3.1、概念 3.2、四个模块 1、 网络状态 2、设备管理 3、应用管理 无人设备接入控制 无线桥接 信号调节…

VS(Visual Studio)更改文件编码

vs默认编码是GB2312,更改为UTF-8 工具->自定义

【数据结构入门精讲 | 第十篇】考研408排序算法专项练习(二)

在上文中我们进行了排序算法的判断题、选择题的专项练习&#xff0c;在这一篇中我们将进行排序算法中编程题的练习。 目录 编程题R7-1 字符串的冒泡排序R7-1 抢红包R7-1 PAT排名汇总R7-2 统计工龄R7-1 插入排序还是堆排序R7-2 龙龙送外卖R7-3 家谱处理 编程题 R7-1 字符串的冒…

Java进阶(第六期): Arrays类(数组工具)、冒泡排序、选择排序、二分查找、【正则表达式】、Java正则爬取信息

文章目录 一、Arrays1.1代码示例&#xff1a; 二、冒泡排序2.1 代码示例 三、选择排序3.1 代码示例 四、二分查找4.1 代码示例 &#xff08;这里采用乱序数组&#xff09; 五、正则表达式5.1 正则表达式的基本使用5.2 正则表达式爬取信息练习 Java进阶&#xff08;第六期&#…

Git的总体认知与具体实现

GIt概念 是一种分布式控制管理器 tips:敏捷开发 -> 先上线&#xff0c;后续开发再继续开发 集中式和分布式 集中式的版本控制系统每次在写代码时都需要从服务器中拉取一份下来&#xff0c;并且如果服务器丢失了&#xff0c;那么所有的就都丢失了&#xff0c;你本机客户端仅…

Web前端-JavaScript(Dom基础)

文章目录 1.1 DOM 介绍1.1.1 DOM简介1.1.2 DOM树 1.2. 获取元素1.2.1 根据ID获取元素1.2.2 根据标签名获取元素1.2.3 其它方式获取元素1.2.4 获取特殊元素 1.3 事件基础1.3.1 事件概述1.3.2 事件三要素1.3.3 执行事件步骤1.3.4 鼠标事件 1.4 操作元素1.4.1 操作元素内容1.4.2 属…

服务器IBM x3650 m2 管理口访问故障处理

服务器的内存告警后&#xff0c;连接管理口查看信息&#xff0c;管理口状态灯显示正常&#xff0c;但是无法ping通和访问。 处理过程如下&#xff1a; 1、在centos 6.6中安装ipmitool&#xff0c;替换为阿里云的yum源&#xff0c;然后安装。 # wget -O /etc/yum.repos.d/Cen…

基于Kubernetes的jenkins上线

1、基于helm 部署jenkins 要求&#xff1a;当前集群配置了storageClass&#xff0c;并已指定默认的storageClass&#xff0c;一般情况下&#xff0c;创建的storageClass即为默认类 指定默认storageClass的方式 # 如果是新创建默认类&#xff1a; apiVersion: storage.k8s.io/v1…

用SQL语句创建数据库表的注意事项

1.所有的符号都要在英文状态下使用。 2.表的名称和字段尽量使用括起来。 3.AUTO_INCREMENT(自增) 4.字符串使用单引号 括起来 5.所有的语句后面加 , (英文的)&#xff0c;最后一个不用加。 6.PRIMARY KEY() 主键&#xff0c;一般一个表只有一个唯一 的主键&#xff01; …

Jenkins自动化部署之后端

准备工作参考本人另外几篇Jenkins相关的文章 新建任务 添加参数配置 字符串参数&#xff1a;分支名称 多选框&#xff1a;项目名称&#xff08;Extended Choice Parameter插件必备&#xff0c;插件安装参考我另外的文章&#xff09; 这个分割规则自定义。只要根据Jenkins…

Go自定义PriorityQueue优先队列使用Heap堆

题目 分析 每次找最大的&#xff0c;pop出来 然后折半&#xff0c;再丢进去 go写法 go如果想用heap&#xff0c;要实现less\len\swap\push\pop 但可以偷懒&#xff0c;用sort.IntSlice,已经实现了less\len\swap 但由于目前是大根堆&#xff0c;要重写一下less 因此&#xff…

懒加载图片案例

整体效果&#xff1a; HTML部分&#xff1a; <div class"lazy-box"><img class"lazy" data-original"img/1.jpg" alt"1.jpg" width"960" height"540"><img class"lazy" data-original…

【RabbitMQ】RabbitMQ详解(二)

RabbitMQ详解 死信队列死信来源消息TTL过期队列达到最大长度消息被拒绝 RabbitMQ延迟队列TTL的两种设置队列设置TTL消息设置TTL 整合SrpingBoot队列TTL延时队列TTL优化Rabbtimq插件实现延迟队列 死信队列 先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就…

Linux---基础操作命令

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

单片机的RTC获取网络时间

理解网络同步校准RTC的原理需要考虑NTP、SNTP、RTC这三个关键组件的作用和交互。下面详细解释这个过程&#xff1a; 1. NTP&#xff08;Network Time Protocol&#xff09;&#xff1a; 协议目的&#xff1a;NTP是用于同步计算机和设备时钟的协议。它通过在网络上与时间服务器通…

JUC AQS ReentrantLock源码分析

AQS java.util.concurrent.locks.AbstractQueuedSynchronizer AQS &#xff08;抽象队列同步器&#xff09;&#xff1a; AbstractQueuedSynchronizer 是什么 来自jdk1.5&#xff0c;是用来实现锁或者其他同步器组件的公共基础部分的抽象实现&#xff0c;是重量级基础框架以及…