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

作者推荐

利用广度优先或模拟解决米诺骨牌

LeetCode1793好子数组的最大分数

给你一个整数数组 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] <<std::endl;
}
}
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<std::pair<int, int>> 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<int, int>& 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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值