首页 > 其他分享 >1793.好子数组的最大分数(力扣每日一题)

1793.好子数组的最大分数(力扣每日一题)

时间:2024-03-19 14:44:06浏览次数:17  
标签:分数 好子 min 1793 nums 力扣 ans 区间 扩充

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], 根据结论2min 值为 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

相关文章

  • 【LeetCode 1793 】好子数组的最高分数
    题目描述原题链接:LeetCode.1793好子数组的最高分数解题思路分数由子数组最小值和长度决定,而且确定了子数组必须包含nums[k]的值;从k位置分别向左向右找更小值就是每个子数组的最小值,左右两端边界下标也就确认了子数组长度;直观朴素的解法是利用单调栈将向左向右分别严格递......
  • 【力扣hot100】49. 字母异位词分组
    题目给你一个字符串数组,请你将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词是由重新排列源单词的所有字母得到的一个新单词。示例1:输入:strs=[“eat”,“tea”,“tan”,“ate”,“nat”,“bat”]输出:[[“bat”],[“nat”,“tan”],[......
  • 力扣---验证二叉搜索树---前根/中根/后根遍历
     题目解析参考:验证二叉搜索树_哔哩哔哩_bilibili一开始做呢,就跟这位老兄一样:因为没有考虑到5和3的比较接下来走入整体:先根遍历解法:首先 每个点其实都有范围,比如根节点的范围在(-INF,INF),就拿上面图片举例子,4这个节点的范围应该是(-INF,5),所以先序遍历,我们要先比较根......
  • 二叉树的广度优先遍历(力扣102)
    文章目录题目前知BFS是什么?队列的基本操作有什么?题解一、思路二、解题方法三、Code总结题目Problem:102.二叉树的层序遍历给你二叉树的根节点root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)。前知BFS是什么?树的广度优先遍历(BFS)是一种遍......
  • 力扣大厂热门面试算法题 39-41
    39.组合总和,40.组合总和II,41.缺失的第一个正数,每题做详细思路梳理,配套Python&Java双语代码,2024.03.17 可通过leetcode所有测试用例。目录39.组合总和解题思路完整代码PythonJava40.组合总和II解题思路完整代码PythonJava41.缺失的第一个正数解题思路完......
  • 力扣周赛第01弹----思维 与 dp
    力扣周赛第01弹----思维与dp一、成为K特殊字符串需要删除的最少字符数题目给你一个字符串word和一个整数k。如果|freq(word[i])-freq(word[j])|<=k对于字符串中所有下标i和j都成立,则认为word是k特殊字符串。此处,freq(x)表示字符x在word中的出现频......
  • 【每日算法】常见AIGC模型; 刷题:力扣单调栈
    上期文章【每日算法】理论:生成模型基础;刷题:力扣单调栈文章目录上期文章一、上期问题二、理论问题1、stablediffusion模型的网络架构2、T5的网络架构(Text-To-TextTransferTransformer模型)3、SDXL模型4、DALLE5、BPE编码6、为什么DDPM加噪声的幅度是不一致的?三、力......
  • Offer必备算法14_哈希表_五道力扣题详解(由易到难)
    目录①力扣1.两数之和解析代码②力扣面试题01.02.判定是否互为字符重排解析代码③力扣217.存在重复元素解析代码④力扣219.存在重复元素II解析代码⑤力扣49.字母异位词分组解析代码本篇完。①力扣1.两数之和1.两数之和难度简单给定一个整数数组 nu......
  • 【力扣】最长公共子序列(动态规划)(还是得学套路才能会做)
    题目描述分析首先容易想出,dp数组的含义应该是两个字符串的最长公共子序列长度。当两个字符串中的任意一个长度为0时,对应的LCS为0由于同时受到两个数组的影响,所以dp数组应该设置为二维数组,并且有:dp[i][j]代表的是s1的0-i的子序列与s2的0-j的子序列的LCS然后分析递推公式:调......
  • 力扣刷题Days19-637.二叉树的层平均数
    目录1,题目2,代码2.1广度优先遍历2.2深度优先遍历3,学习与总结1,题目给定一个非空二叉树的根节点 root ,以数组的形式返回每一层节点的平均值。2,代码2.1广度优先遍历/***Definitionforabinarytreenode.*functionTreeNode(val,left,right){*......