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的控制),原因在于:
max()
方法能够保持负数归零- 更新变量的顺序能够保证每次k都能在最右端,而i和j在k左端(更新的滞后性)
- i和j的顺序同理