📜  最大化严格增加或减少的子阵列的乘积(1)

📅  最后修改于: 2023-12-03 15:10:34.846000             🧑  作者: Mango

最大化严格增加或减少的子阵列的乘积

简介

给定一个长度为 $n$ 的正整数数组 $a$,我们希望选取其长度尽可能大的一个子数组,请你计算这个子数组的元素的积。

需要满足的条件是这个子数组先是严格单调递增的,然后是严格单调递减的。

解题思路

我们考虑如何找到这个子数组:

  1. 首先对于每一个位置,我们计算出它往左和往右分别有多少个数严格小于它。这可以用单调栈来实现。

  2. 对于每一个位置,我们可以把这个位置往左和往右的数按严格单调递增和严格单调递减的顺序排列。这是一个经典的 LIS 问题,可以用线段树实现。

  3. 最后,对于每一个位置,我们将它往左和往右的严格单调递增和严格单调递减的序列合并,然后计算出它们的乘积。

最终的答案就是所有位置的答案的最大值。

代码实现
class SegmentTree:
    def __init__(self, A, topn=0):
        self.N = len(A)
        self.nodes = [0] * (2 * self.N)
        if topn:
            self.tree = [set([A[i]]) for i in range(self.N)]
            self.topn = topn
        else:
            self.tree = [A[i] for i in range(self.N)]
        
        for i in range(self.N-1, 0, -1):
            self.tree[i] = self.tree[i<<1] + self.tree[i<<1|1]
    
    def __setitem__(self, idx, val):
        idx += self.N
        self.tree[idx] = {val}
        while idx > 1:
            idx >>= 1
            self.tree[idx] = self.tree[idx<<1] + self.tree[idx<<1|1]
    
    def query(self, l, r):
        l += self.N
        r += self.N
        res = set() if self.topn else 1
        while l < r:
            if l & 1:
                res = res + self.tree[l]
                l += 1
            if r & 1:
                r -= 1
                res = res + self.tree[r]
            l >>= 1
            r >>= 1
        return res

    def __getitem__(self, idx):
        return self.tree[idx+self.N]
    
class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        n = len(nums)
        left_le = SegmentTree([-1]*n)
        left_g = SegmentTree([-1]*n)
        right_le = SegmentTree([n]*n)
        right_g = SegmentTree([n]*n)
        product = SegmentTree(nums)
        
        s1 = []
        s2 = []
        for i in range(n):
            while s1 and nums[s1[-1]] >= nums[i]:
                s1.pop()
            left_le[i] = s1[-1] if s1 else -1
            left_g[i] = s2[-1] if s2 else -1
            s1.append(i)
            while s2 and nums[s2[-1]] < nums[i]:
                s2.pop()
            s2.append(i)
        
        s1 = []
        s2 = []
        for i in range(n-1, -1, -1):
            while s1 and nums[s1[-1]] >= nums[i]:
                s1.pop()
            right_le[i] = s1[-1] if s1 else n
            right_g[i] = s2[-1] if s2 else n
            s1.append(i)
            while s2 and nums[s2[-1]] < nums[i]:
                s2.pop()
            s2.append(i)
        
        ans = 1
        for i in range(n):
            l_le = left_le[i] if left_le[i] != -1 else 0
            r_g = right_g[i] if right_g[i] != n else n-1
            if l_le >= r_g:
                continue
            cur_le = list(product.query(l_le, i))
            cur_g = list(product.query(i, r_g))
            cur_le.sort()
            cur_g.sort(reverse=True)
            ret = 1
            cnt = 0
            for x, y in zip(cur_le, cur_g):
                if cnt == 0:
                    ret = x * y
                elif cnt == 1:
                    ret *= x * y
                elif len(cur_le) + len(cur_g) - cnt <= 2:
                    ret *= x * y
                    break
                cnt += 1
            ans = max(ans, ret)
        
        return ans

代码片段使用 Markdown 格式标明。