1793.好子数组的最大分数
给你一个整数数组 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
题解(双指针+贪心):
由题意可知,最大分数的区间内必定包含 nums[k] ,我们考虑从这个数开始扩充区间,即处理包含这个数的所有区间,记录这些区间的最大分数即可。
初始区间的分数为:min(nums[k]) * 1(区间长度)
我们先考虑向右边(或者左边,二者对等)扩充区间:
1、若 nums[k + 1] >= nums[k], 显然 min 的值没有改变,而且区间长度增加了,即此时的区间分数为:nums[k] * 2
2、若 nums[k + 1] < nums[k], 此时若要把这个数添加到区间内,则必定会改变min的值,但是区间长度增加了,此时的区间分数为:nums[k + 1] * 2
同理,区间向左边扩充也是一样的,但如何决定先向左边还是右边扩充呢?
若nums[k - 1] >= nums[k] && nums[k + 1] >= nums[k], 正如1中所描述,这种情况直接扩充即可,即向左向右都进行扩充区间操作,直到左边或者右边都出现小于 nums[k] 的数。
此时,nums[k - 1] < nums[k] && nums[k +1] < nums[k] 我们要考虑先向左还是先向右扩充的最优性。
分析可知,显然:
结论1: 在区间长度不变的情况下, min值越大分数越高
结论2:在min值不变的情况下,区间长度越长,分数越高
我们主要考虑 nums[k - 1] != nums[k + 1] 这种情况(相等的时候随便选择一个更新min值都可以),无论我们向哪边扩充区间长度都是+1,对于相同区间长度,min的值越大则分数越大,因此我们选取nums[k - 1] 和 nums[k + 1]中较大的那个值肯定是最优的,因为它最终的分数肯定是大于另一个的。
至此,我们已经考虑了所有最优扩充区间的情况,接下来只需要考虑维护当前区间的分数即可,为了比较好的分辨出当前的区间,可以考虑使用双指针,即i代表区间的头部,j代表区间的尾部。
那么区间长度就能很容易得到,即 j - i + 1 。
然后我们只需要知道当前区间的 min 值就能解决问题啦。
min 的初始值是 nums[k],根据上面对区间扩充的分析,min 的值发生改变只有当向左或者向右扩充时,新增加的元素值小于 min 时才会发生改变,此时更新min值即可。
举个例子:
输入:nums = [1,4,3,7,4,5], k = 3
nums[k] = 7, 初始区间min值为 7
我们首先扩充区间,但确保 min 值不变,显然这无法做到,根据结论1,那我们记录此时的分数,即 min 值为 7 时的最大分数,score = 7 * 1 = 7 。
接下来要扩充区间只能改变min值,左边为3, 右边为4 ,根据结论2我们选择 4 肯定是最优的,然后我们考虑 min 值为 4 的区间长度最大是多少,即按照上文的方法扩充区间。
此时得到区间 [7,4,5], 根据结论2, min 值为 4 时这种情况分数最大,score = 4 * 3 = 12 。
同理下一个扩充区间为 [4,3,7,4,5] ,此时 min 值为 3 的这种情况分数最大, score = 3 * 5 = 15 。
.......
我们依次扩充区间记录每次min值改变的最长区间的分数,保存较大值即可。
最后编写我们的代码,记得考虑边界情况。
//总体时间复杂度O(N)
class Solution {
public:
int a[100010];//个人比较习惯用数组
int maximumScore(vector<int>& nums, int k) {
for(int i = 0; i < nums.size(); i ++) a[i + 1] = nums[i];//存到数组中,下标1开始,避免越界
k ++;//因为a数组是下标1开始的所以nums[k] = a[k + 1]
int i = k - 1, j = k + 1, ans = 0, n = nums.size(), p = a[k];//i表示起点,j表示终点,ans存储分数最大值,p记录区间min值
//左右不改变min值情况下扩充区间,得到min值为a[k]的最长区间长度
while(a[i] >= a[k] && i > 0)
{
i --;
}
while(a[j] >= p && j <= n)
{
j ++;
}
//记录分数
ans = p * (j - i - 1);
while(i > 0 || j <= n)//一直扩充直到不能再扩充
{
if(i > 0 && j <= n)//判断下标是否到边界值
{
//记录二则最优的min值
if(a[i] > a[j]) p = a[i];
else p = a[j];
//无脑扩充长度
while(i > 0 && a[i] >= p)
{
i --;
}
while(a[j] >= p && j <= n)
{
j ++;
}
//记录分数
ans = max(ans, p * (j - i - 1));
}
else if(i > 0)//右边不能再扩充,只能扩充左边
{
p = a[i];
while(i > 0 && a[i] >= p)
{
i --;
}
//记录分数
ans = max(ans, p * (j - i - 1));
}
else if(j <= n)//左边不能再扩充,只能扩充右边
{
p = a[j];
while(a[j] >= p && j <= n)
{
j ++;
}
//记录分数
ans = max(ans, p * (j - i - 1));
}
}
return ans;
}
};
标签:分数,好子,min,1793,nums,力扣,ans,区间,扩充 From: https://www.cnblogs.com/wockcow/p/18082806