【算法刷题-第10题】元素和最小的山形三元组 II-力扣周赛

题目

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

如果下标三元组 (i, j, k) 满足下述全部条件,则认为它是一个 山形三元组 :

  • i < j < k
  • nums[i] < nums[j] 且 nums[k] < nums[j]

请你找出 nums 中 元素和最小 的山形三元组,并返回其 元素和 。如果不存在满足条件的三元组,返回 -1 。

样例

示例 1:

输入:nums = [8,6,1,5,3]
输出:9
解释:三元组 (2, 3, 4) 是一个元素和等于 9 的山形三元组,因为: 
- 2 < 3 < 4
- nums[2] < nums[3] 且 nums[4] < nums[3]
这个三元组的元素和等于 nums[2] + nums[3] + nums[4] = 9 。可以证明不存在元素和小于 9 的山形三元组。

示例 2:

输入:nums = [5,4,8,7,10,2]
输出:13
解释:三元组 (1, 3, 5) 是一个元素和等于 13 的山形三元组,因为: 
- 1 < 3 < 5 
- nums[1] < nums[3] 且 nums[5] < nums[3]
这个三元组的元素和等于 nums[1] + nums[3] + nums[5] = 13 。可以证明不存在元素和小于 13 的山形三元组。

示例 3:

输入:nums = [6,5,4,3,4,5]
输出:-1
解释:可以证明 nums 中不存在山形三元组。
  • 3 <= nums.length <= 105
  • 1 <= nums[i] <= 108

题解

1. 这题一开始想的是直接暴力枚举所有数,如果数据范围小的话,是可以过的,但是这题的数据范围在1e5了,所以暴力循环肯定是会超时的

暴力代码

class Solution:
    def minimumSum(self, nums: List[int]) -> int:
        lst = []
        for i in range(0,len(nums)-2):
            for j in range(i+1,len(nums)-1):
                for k in range(j+1,len(nums)):
                    if i 0:
            return min(lst)
        return -1

                

2. 然后是需要进行优化的,要保持在O(n)左右基本才可以

3. 这该如何优化呢?

4. 我们通过题目可以得出,

  • i < j < k
  • nums[i] < nums[j] 且 nums[k] < nums[j]

这里面主要就是j在中间,然后i,k的数要比j值小

5. 这样看的话,其实我们只需要枚举这个j值就可以了,然后把j的左边和j的右边分成两个部分

6. 要让值最小,且要满足条件,也就是,左边的最小值肯定是要比j的值要小的,如果左边最小的值比j值还要大的话,那肯定是不满足条件的,右边也是如此

7. 这个时候我们就要去枚举j值,然后看左边最小的和右边的最小值,是否都满足条件,如果满足条件的话,就可以相加,然后每次求出一个总和,这个值就是满足条件的和值,然后最后在这些值里面找一个最小的值就可以了

8. 然后现在的问题就是如何找到左边和右边的最小值,难道要每遍历一个j值,就要左边和右边去找一个i,k值吗?

9, 行不行,我们先看代码:

class Solution:
    def minimumSum(self, nums: List[int]) -> int:
        minn = inf
        # pre = nums[:]
        # ba = nums[:]
        for i in range(1,len(nums)):
            pre = min(nums[:i])
            ba = min(nums[i:])
            if nums[i] > max(pre,ba):
                minn = min(minn,nums[i] + pre + ba)
        if minn < inf:
            return minn
        return -1
        
        

这样的结果其实超时的,在你遍历到j的时候,又要去遍历前面的数,一直在不断重复遍历

10. 然后这个时候我们又要进一步优化,也就是我们可以提前用一个列表进行保存,用两个列表裂变进行保存之前最小的值

11. 也就是当我们遍历到j的时候,这个时候,我们提前声明了一个pre的列表,pre[j]的值就是在j前面的最小值,再声明一个ba列表,表示后面最小的值,这个我们最后判断的时候,就看我们的nums[j]的值是否大于pre[j]和ba[j]的值了

12. 那我们应该如何来保存这个pre和ba呢,这个时候就要采用前缀和的思想了,我们的前缀和就是保存前面和当前的和值

13. 也就是从第二个数开始,判断当前的数和前面一个数(表示前面所有值的最小值)的值谁小,谁小的话就保存进去,然后我们的ba也是如此,ba要从后面开始遍历,因为要查看j后面的最小值

14. 最后我们通过pre和nums和ba的关系,就可以得出最后的代码了

代码

class Solution:
    def minimumSum(self, nums: List[int]) -> int:
        minn = inf
        pre = nums[:]   
        ba = nums[:]
        for i in range(1,len(nums)):
            pre[i] = min(pre[i],pre[i-1])   # 前面的最小值
        for i in range(len(nums)-2,-1,-1):
            ba[i] = min(ba[i],ba[i+1])     # 后面的最小值
        for i in range(1,len(nums)):
            if nums[i] > max(pre[i],ba[i]):
                minn = min(minn,pre[i] +ba[i] + nums[i])   # 求最小值
        if minn < inf:
            return minn
        return -1
        
        

这个地方要注意一下,为什么要pre = nums[:]呢,因为这个地方,如果你直接=nums的话,他是直接将pre指向和nums的同一片区域,也就是你pre里面的值改变的话,nums的值也会改变,所以这个地方应该直接把nums里面的值重新赋值给pre,也就是创建了一个新的区域,而不是引用同一片区域

你可能感兴趣的:(算法题解,算法,leetcode,数据结构)