Leetcode刷题Day4

2873. 有序三元组中的最大值 I

给你一个下标从 0 开始的整数数组 nums

请你从所有满足 i < j < k 的下标三元组 (i, j, k) 中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回 0

下标三元组 (i, j, k) 的值等于 (nums[i] - nums[j]) * nums[k]

示例 1:

输入:nums = [12,6,1,2,7]
输出:77
解释:下标三元组 (0, 2, 4) 的值是 (nums[0] - nums[2]) * nums[4] = 77 。
可以证明不存在值大于 77 的有序下标三元组。

示例 2:

输入:nums = [1,10,3,4,19]
输出:133
解释:下标三元组 (1, 2, 4) 的值是 (nums[1] - nums[2]) * nums[4] = 133 。
可以证明不存在值大于 133 的有序下标三元组。 

示例 3:

输入:nums = [1,2,3]
输出:0
解释:唯一的下标三元组 (0, 1, 2) 的值是一个负数,(nums[0] - nums[1]) * nums[2] = -3 。因此,答案是 0 。

提示:

  • 3 <= nums.length <= 100
  • 1 <= nums[i] <= 1 0 6 10^6 106

思路
这题乍一看,才三元组,直接三重for循环暴力枚举i、j、k,再一看数据,哇nums.lenth居然只是 1 0 2 10^2 102级别的,直接暴力秒了!但还是之前那句话,这样做题不会有任何提升,我们来看看它是否有更好的优化空间,将 O ( n 3 ) O(n^3) O(n3)优化为 O ( n ) O(n) O(n)
首先,对于枚举类问题,我们一个主要思路是枚举的对象越少,复杂度也就越低,因此我们可以从枚举对象入手。我们可以发现,并不是一定要枚举i、j、k,因为移动它们三个的目的是为了寻找 (nums[i] - nums[j]) * nums[k] 的最值,我们发现只要让 (nums[i] - nums[j])nums[k] 都尽可能大,那么最终结果也会最大,但是又不能重复取, (nums[i] - nums[j])最大就无法保证nums[k]最大,如何表示这种“尽可能大”呢?我们可以选择“定一移二”的策略,也就是说,将j固定,移动i和k:具体来说就是在整个区间上确定一个j,在区间[0,j)上找到一个nums[i],固定住j只要让nums[i]最大,那么 (nums[i] - nums[j])也就最大,同理在(j,nums.size()-1]上找到一个最大的nums[k],从而保证整体最大。从而我们可以总结出三元组问题的一般思想:定中间的元素,操作两端的元素,这也是选择操作j的原因。但是这总方法需要两个数组分别记录j左边的区间最大值和j右边的区间最大值,虽然可以将时间复杂度优化为 O ( n ) O(n) O(n),但是空间上的复杂度却退化为了 O ( n ) O(n) O(n),因此这种方法大家可以自己尝试一下,本文不再给出实现的代码。

最优解思路
基于上文的“定一移二”,我们可以想到另外一种空间开销较小的方法,也就是定住k,移动i、j让 (nums[i] - nums[j])最大。为什么要用这种方法呢?我们回想一下刚才的方法,发现其实我们记录的内容并不是全部都用得上,最后只会使用那个最大的值,因此这个过程中造成了数据冗余,所以考虑只使用一个变量来记录最值。

代码

class Solution {
public:
    long long maximumTripletValue(vector<int>& nums) {
        long long ans=0,idxMax=0,diffMax=0;
        for(int k=0;k<nums.size();++k){
            ans = max(ans,nums[k]*diffMax);
            diffMax = max(diffMax,idxMax-nums[k]);
            idxMax = max(idxMax,static_cast<long long>(nums[k]));
        }
        return ans;
    }
};

注意

本段代码的巧妙之处在于以一“k”动“i、j”(就是说靠着k可以完成对i、j的控制),原因在于:

  1. max()方法能够保持负数归零
  2. 更新变量的顺序能够保证每次k都能在最右端,而i和j在k左端(更新的滞后性)
  3. i和j的顺序同理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值