C++单调向量(栈):好子数组的最大分数

简介: C++单调向量(栈):好子数组的最大分数

题目

给你一个整数数组 nums (下标从 0 开始)和一个整数 k 。

一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], …, nums[j]) * (j - i + 1) 。一个 好 子数组的两个端点下标需要满足 i <= k <= j 。

请你返回 好 子数组的最大可能 分数 。

示例 1:

输入:nums = [1,4,3,7,4,5], k = 3

输出:15

解释:最优子数组的左右端点下标是 (1, 5) ,分数为 min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15 。

示例 2:

输入:nums = [5,5,4,5,4,1,1,1], k = 0

输出:20

解释:最优子数组的左右端点下标是 (0, 4) ,分数为 min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20 。

参数

1 <= nums.length <= 105

1 <= nums[i] <= 2 * 104

0 <= k < nums.length

分析

时间复杂度

O(n)。

步骤

一,寻找nums[i]的右边界,数组边界或从左向右第一个小于nums[i]的数。如果i1小于i2,且nums[i1] <= nums[i2],则i1淘汰了i2,淘汰后:i降序,nums[i]升序。

二,寻找nums[i]的左边界,数组左边界或从右向左,第一个小于nums[i]的数。

三,左开右开区间(vLeft[i],vRight[i]) 就是以nums[i]为最小值的区间。如果k在这个区间,则更新返回值。

代码

核心代码

class Solution {
public:
int maximumScore(vector& nums, int k) {
m_c = nums.size();
vector vRight(m_c, m_c);
{
vector vIndexs;
for (int i = m_c-1 ; i >= 0 ; i-- )
{
while (vIndexs.size() && (nums[vIndexs.back()] >= nums[i]))
{
vIndexs.pop_back();
}
if (vIndexs.size())
{
vRight[i] = vIndexs.back();
}
vIndexs.emplace_back(i);
}
}
vector vLeft(m_c, -1);
{
vector vIndexs;
for (int i = 0; i < m_c; i++)
{
while (vIndexs.size() && (nums[vIndexs.back()] >= nums[i]))
{
vIndexs.pop_back();
}
if (vIndexs.size())
{
vLeft[i] = vIndexs.back();
}
vIndexs.emplace_back(i);
}
}
int iRet = 0;
for (int i = 0; i < m_c; i++)
{
if ((k > vLeft[i]) && (k < vRight[i]))
{
iRet = max(iRet, nums[i] * (vRight[i] - vLeft[i] - 1));
std::cout << i << " nums[i]:" << nums[i] << " " << vLeft[i] << " " << vRight[i] <
}
}
return iRet;
}
int m_c;
};

优化:寻找边界循环一次

左边界 数组边界或小于nums[i]
右边界 数组边界或小于等于nums[i]

改变规则后:寻找左边界淘汰vIndexs时,说明:i是vIndexs.back()的第一个小于等于的数,也就是右边界。

题外话

解法一会造成重复,本题是求最大值,重复不会影响结果。求和就会有影响了。

比如:{1,1}

解法一 解法二
i=0 {1,1} {1}
i=1 {1,1} {1,1}

拆开后:

解法一: {1},{1},{1,1},{1,1}
解法二: {1},{1,1},{1}

代码

class Solution {
public:
  int maximumScore(vector<int>& nums, int k) {
    m_c = nums.size();
    vector<int> vRight(m_c, m_c);
    vector<int> vLeft(m_c, -1);
    vector<int> vIndexs;
    for (int i = 0; i < m_c; i++)
    {
      while (vIndexs.size() && (nums[vIndexs.back()] >= nums[i]))
      {
        vRight[vIndexs.back()] = i;
        vIndexs.pop_back();
      }
      if (vIndexs.size())
      {
        vLeft[i] = vIndexs.back();
      }
      vIndexs.emplace_back(i);
    }
    int iRet = 0;
    for (int i = 0; i < m_c; i++)
    {
      if ((k > vLeft[i]) && (k < vRight[i]))
      {
        iRet = max(iRet, nums[i] * (vRight[i] - vLeft[i] - 1));
        std::cout << i << " nums[i]:" << nums[i] << " " << vLeft[i]  << " " << vRight[i] <<std::endl;
      }
    }
    return iRet;
  }
  int m_c;
};

2023年3月旧代码

class Solution {
public:
int maximumScore(vector& nums, int k) {
m_c = nums.size();
vector> staLeft,staRight;
{
for (int i = 0; i < k; i++)
{
while (staLeft.size() && (staLeft.back().first >= nums[i]))
{
staLeft.pop_back();
}
staLeft.emplace_back(nums[i], i);
}
}
{
for (int i = nums.size() - 1; i > k; i–)
{
while (staRight.size() && (staRight.back().first >= nums[i]))
{
staRight.pop_back();
}
staRight.emplace_back(nums[i], i);
}
}
auto CmpFun = [](const std::pair& p, int iCmp)
{return p.first < iCmp; };
int iMaxRet = 0 ;
for (int iValue = 1; iValue <= nums[k]; iValue++)
{
auto it = std::lower_bound(staLeft.begin(), staLeft.end(), iValue, CmpFun);
int iLeft = (staLeft.begin() == it) ? -1 : (–it)->second;
auto it2 = std::lower_bound(staRight.begin(), staRight.end(), iValue, CmpFun);
int iRight = (staRight.begin() == it2) ? m_c : (–it2)->second;
iMaxRet = max(iMaxRet, iValue* (iRight - iLeft - 1));
}
return iMaxRet;
}
int m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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




相关文章
|
3月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
234 77
|
3月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
117 7
|
3月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
137 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
3月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
78 9
|
6月前
|
算法 C++
|
6月前
|
算法 C++
【算法单调栈】 矩形牛棚(C/C++)
【算法单调栈】 矩形牛棚(C/C++)
|
8月前
|
C++
C++ PCL 将一个点云投影到一个由法向量和点确定的平面
C++ PCL 将一个点云投影到一个由法向量和点确定的平面
232 0
|
8月前
|
传感器 算法 C++
C++ PCL 设置法向量的方向
C++ PCL 设置法向量的方向
144 0
|
9月前
|
vr&ar C++
1695. 删除子数组的最大得分(C++,滑动窗口)
1695. 删除子数组的最大得分(C++,滑动窗口)
|
9月前
|
C++
2461. 长度为 K 子数组中的最大和(c++)
2461. 长度为 K 子数组中的最大和(c++)

热门文章

最新文章