题目的大致描述如下:
给你一个下标从 0 开始的整数数组 nums 。
如果下标三元组 (i, j, k) 满足下述全部条件,则认为它是一个 山形三元组 :i < j < k
,nums[i] < nums[j] 且 nums[k] < nums[j]
请你找出 nums 中 元素和最小的山形三元组,并返回其元素和 。如果不存在满足条件的三元组,返回 -1 。
简单分析一下这个题目的思路。
1.题意很简单,大家估计都能看懂,我们第一时间最直观的想法就是三层for循环,分别遍历每个i,j,k然后判断合理性即可找到满足要求的最小值,如果找不到返回-1即可.但是这种做法的时间复杂度达到了O(n^3),这显然不是一个可以让人接受的时间复杂度,对于n=1000的数据都会超时(1e8),所以我们考虑如何用优化。
2.其实这个题目大家看一下它的题目,我们可以发现这样一个问题,每次是不是就是去找每一个元素的左侧和右侧的最小元素,然后比较一下和当前元素,如果都比当前位置i的元素nums[i]要小,那么就是一个合法的元素我们就可以进行更新最终结果,那么现在的问题是如何去保存前缀和后缀最小值,这个其实很简单我们只需要从后向前以及从前向后遍历一遍,利用一个数组来保存从当前位置到第1个元素位置的最小值,到最后也同理。但是这里我们还可以做一个优化就是,我们只需要求后缀最小值,前缀最小值可以在for循环遍历过程,边遍历边更新,初始化就是nums[0]
枚举 nums[j],我们需要求出 j 左边所有元素的最小值和右边所有元素的最小值。
这可以递推计算。定义 back[i]表示从 nums[i]到 nums[n−1]的最小值(后缀最小值),则有back[i]=min(back[i+1],nums[i])
前缀最小值 pre的计算方式同理,可以和答案一起算,所以只需要一个变量。
那么答案就是:**pre+nums[j]+back[j+1]**的最小值
具体实现代码如下:(相关注释已经标注)
int minimumSum(vector<int>& nums) {
int n = nums.size();
vector<int>back(n);
back[n - 1] = nums[n - 1];//初始化第1个back数组 也就是后缀最小值为nums的最后一个元素
for(int i = n - 2;i > 0;i--){
if(nums[i] < back[i + 1]){
back[i] = nums[i];//如果遇到更小的 更新当前back[i]=nums[i]
}else{
back[i] = back[i + 1];//否则就是back[i+1]
}
}
int pre = nums[0],minn = 205;//前缀最小初始为nums[0],然后遍历过程不断更新
for(int i = 1;i < n - 1;i++){
if(nums[i] > pre && nums[i] > back[i + 1]){
minn = min(minn, nums[i] + pre + back[i + 1]);
}
pre = min(pre, nums[i]);
}
return minn == 205 ? -1:minn;
}