第 120 场双周赛(前缀和,双指针,树形dp+贪心)

发布时间 2023-12-24 20:02:16作者: 深渊之巅

 

class Solution:
    def largestPerimeter(self, nums: List[int]) -> int:
        nums.sort()
        n = len(nums)
        s = list(accumulate(nums))
        for i in range(n - 1, 1, -1):
            if nums[i] < s[i - 1]:
                return nums[i] + s[i - 1]
        
        return -1

 

 

思考问题: 特殊 -> 一般,不熟悉的 -> 学过的

 统计子数组个数,固定一个端点,计算另一个端点个数。

设我们移除中间[l, r]的子数组之后满足题意,左侧 [0, l) 是递增的, 右侧(r, n - 1] 也是递增的, 此时我们可以移除 [l-1, r], [l-2, r] ... [0, r] 都是可以的,及ans += l + 2

再来考虑右端点如何移动,我们的前提是去掉中间左侧和右侧都是递增的,及当 a[j] < a[j + 1] 的时候,我们就可以将j左移。

class Solution:
    def incremovableSubarrayCount(self, a: List[int]) -> int:
        n = len(a)
        i = 0

        while i < n - 1 and a[i] < a[i + 1]:
            i += 1
        
        if i == n - 1:
            return n * (n + 1) // 2
        
        ans = i + 2
        j = n - 1
        while j > 0 and (j == n - 1 or a[j] < a[j + 1]):
            while i >= 0 and a[i] >= a[j]:
                i -= 1
            ans += i + 2
            j -= 1

        return ans

 

 

 树形dp问题,dfs

在数组中,要想是三个数乘积最大,可以选三个最大的正数,也可以选两个最小的负数,一个最大的正数。

我们采用自底向上的思想,先递归到叶子结点,在回溯过程中我们将子数组最大的三个数和最小的两个数返回给父节点。这样父节点就可以根据子节点提供的信息进行代价的计算。

class Solution:
    def placedCoins(self, edges: List[List[int]], cost: List[int]) -> List[int]:
        n = len(cost)
        ans = [1] * n
        g = [[] for _ in range(n)]

        for a, b in edges:
            g[a].append(b)
            g[b].append(a)

        def dfs(u: int, fa: int) -> List[int]:
            a = [cost[u]]
            for y in g[u]:
                if y != fa:
                    a.extend(dfs(y, u))
            
            a.sort()
            m = len(a)
            if m >= 3:
                ans[u] = max(a[-3] * a[-2] * a[-1], a[0] * a[1] * a[-1], 0)
            if m > 5:
                a = a[:2] + a[-3:]

            return a
        
        dfs(0, -1)
        return ans