LeetCode 题解工作台

将数组分成最小总代价的子数组 II

给你一个下标从 0 开始长度为 n 的整数数组 nums 和两个 正 整数 k 和 dist 。 一个数组的 代价 是数组中的 第一个 元素。比方说, [1,2,3] 的代价为 1 , [3,4,1] 的代价为 3 。 你需要将 nums 分割成 k 个 连续且互不相交 的 子数组 ,满足 第二 个…

category

4

题型

code_blocks

6

代码语言

hub

3

相关题

当前训练重点

困难 · 数组·哈希·扫描

bolt

答案摘要

题目需要我们将数组 分割成 个连续且不相交的子数组,并且第二个子数组的第一个元素与第 个子数组的第一个元素的下标距离不超过 ,这等价于让我们从 下标为 的元素开始,找到一个窗口大小为 的子数组,求其中前 的最小元素之和。我们将 减 ,这样我们只需要求出 个最小元素之和,再加上 即可。 我们可以使用两个有序集合 和 分别维护大小为 $\textit{dist} + 1$ 的窗…

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 数组·哈希·扫描 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个下标从 0 开始长度为 n 的整数数组 nums 和两个  整数 k 和 dist 。

一个数组的 代价 是数组中的 第一个 元素。比方说,[1,2,3] 的代价为 1 ,[3,4,1] 的代价为 3 。

你需要将 nums 分割成 k 个 连续且互不相交 的子数组,满足 第二 个子数组与第 k 个子数组中第一个元素的下标距离 不超过 dist 。换句话说,如果你将 nums 分割成子数组 nums[0..(i1 - 1)], nums[i1..(i2 - 1)], ..., nums[ik-1..(n - 1)] ,那么它需要满足 ik-1 - i1 <= dist 。

请你返回这些子数组的 最小 总代价。

 

示例 1:

输入:nums = [1,3,2,6,4,2], k = 3, dist = 3
输出:5
解释:将数组分割成 3 个子数组的最优方案是:[1,3] ,[2,6,4] 和 [2] 。这是一个合法分割,因为 ik-1 - i1 等于 5 - 2 = 3 ,等于 dist 。总代价为 nums[0] + nums[2] + nums[5] ,也就是 1 + 2 + 2 = 5 。
5 是分割成 3 个子数组的最小总代价。

示例 2:

输入:nums = [10,1,2,2,2,1], k = 4, dist = 3
输出:15
解释:将数组分割成 4 个子数组的最优方案是:[10] ,[1] ,[2] 和 [2,2,1] 。这是一个合法分割,因为 ik-1 - i1 等于 3 - 1 = 2 ,小于 dist 。总代价为 nums[0] + nums[1] + nums[2] + nums[3] ,也就是 10 + 1 + 2 + 2 = 15 。
分割 [10] ,[1] ,[2,2,2] 和 [1] 不是一个合法分割,因为 ik-1 和 i1 的差为 5 - 1 = 4 ,大于 dist 。
15 是分割成 4 个子数组的最小总代价。

示例 3:

输入:nums = [10,8,18,9], k = 3, dist = 1
输出:36
解释:将数组分割成 3 个子数组的最优方案是:[10] ,[8] 和 [18,9] 。这是一个合法分割,因为 ik-1 - i1 等于 2 - 1 = 1 ,等于 dist 。总代价为 nums[0] + nums[1] + nums[2] ,也就是 10 + 8 + 18 = 36 。
分割 [10] ,[8,18] 和 [9] 不是一个合法分割,因为 ik-1 和 i1 的差为 3 - 1 = 2 ,大于 dist 。
36 是分割成 3 个子数组的最小总代价。

 

提示:

  • 3 <= n <= 105
  • 1 <= nums[i] <= 109
  • 3 <= k <= n
  • k - 2 <= dist <= n - 2
lightbulb

解题思路

方法一:有序集合

题目需要我们将数组 nums\textit{nums} 分割成 kk 个连续且不相交的子数组,并且第二个子数组的第一个元素与第 kk 个子数组的第一个元素的下标距离不超过 dist\textit{dist},这等价于让我们从 nums\textit{nums} 下标为 11 的元素开始,找到一个窗口大小为 dist+1\textit{dist}+1 的子数组,求其中前 k1k-1 的最小元素之和。我们将 kk11,这样我们只需要求出 kk 个最小元素之和,再加上 nums[0]\textit{nums}[0] 即可。

我们可以使用两个有序集合 l\textit{l}r\textit{r} 分别维护大小为 dist+1\textit{dist} + 1 的窗口元素,其中 l\textit{l} 维护最小的 kk 个元素,而 r\textit{r} 维护窗口的剩余元素。我们维护一个变量 s\textit{s} 表示 nums[0]\textit{nums}[0]ll 中元素之和。初始时,我们将前 dist+2\textit{dist}+2 个元素之和累加到 s\textit{s} 中,并将下标为 [1,dist+1][1, \textit{dist} + 1] 的所有元素加入到 l\textit{l} 中。如果 l\textit{l} 的大小大于 kk,我们循环将 l\textit{l} 中的最大元素移动到 r\textit{r} 中,直到 l\textit{l} 的大小等于 kk,过程中更新 s\textit{s} 的值。

那么此时初始答案 ans=s\textit{ans} = \textit{s}

接下来我们从 dist+2\textit{dist}+2 开始遍历 nums\textit{nums},对于每一个元素 nums[i]\textit{nums}[i],我们需要将 nums[idist1]\textit{nums}[i-\textit{dist}-1]l\textit{l}r\textit{r} 中移除,然后将 nums[i]\textit{nums}[i] 加入到 l\textit{l}r\textit{r} 中。如果 nums[i]\textit{nums}[i] 小于 l\textit{l} 中的最大元素,我们将 nums[i]\textit{nums}[i] 加入到 l\textit{l} 中,否则加入到 r\textit{r} 中。如果此时 l\textit{l} 的大小小于 kk,我们将 r\textit{r} 中的最小元素移动到 l\textit{l} 中,直到 l\textit{l} 的大小等于 kk。如果此时 l\textit{l} 的大小大于 kk,我们将 l\textit{l} 中的最大元素移动到 r\textit{r} 中,直到 l\textit{l} 的大小等于 kk。过程中更新 s\textit{s} 的值,并更新 ans=min(ans,s)\textit{ans} = \min(\textit{ans}, \textit{s})

最终答案即为 ans\textit{ans}

时间复杂度 O(n×logdist)O(n \times \log \textit{dist}),空间复杂度 O(dist)O(\textit{dist})。其中 nn 为数组 nums\textit{nums} 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution:
    def minimumCost(self, nums: List[int], k: int, dist: int) -> int:
        def l2r():
            nonlocal s
            x = l.pop()
            s -= x
            r.add(x)

        def r2l():
            nonlocal s
            x = r.pop(0)
            l.add(x)
            s += x

        k -= 1
        s = sum(nums[: dist + 2])
        l = SortedList(nums[1 : dist + 2])
        r = SortedList()
        while len(l) > k:
            l2r()
        ans = s
        for i in range(dist + 2, len(nums)):
            x = nums[i - dist - 1]
            if x in l:
                l.remove(x)
                s -= x
            else:
                r.remove(x)
            y = nums[i]
            if y < l[-1]:
                l.add(y)
                s += y
            else:
                r.add(y)
            while len(l) < k:
                r2l()
            while len(l) > k:
                l2r()
            ans = min(ans, s)
        return ans
speed

复杂度分析

指标
时间Depends on the final approach
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Ability to apply array scanning to partitioning problems.

  • question_mark

    Efficiency in using hash tables for dynamic lookups.

  • question_mark

    Understanding of sliding window techniques and their optimizations.

warning

常见陷阱

外企场景
  • error

    Incorrectly dividing the array without checking the distance constraint for each subarray.

  • error

    Inefficiently recalculating costs for overlapping subarrays.

  • error

    Failing to optimize the partition points dynamically, leading to higher computational complexity.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Adjust the problem by increasing the number of subarrays (k) or reducing the allowed distance (dist).

  • arrow_right_alt

    Allow some overlap between subarrays to explore different partitioning strategies.

  • arrow_right_alt

    Introduce additional constraints such as limiting the maximum or minimum value of elements within subarrays.

help

常见问题

外企场景

将数组分成最小总代价的子数组 II题解:数组·哈希·扫描 | LeetCode #3013 困难