LeetCode 题解工作台

拆分数组的最小代价

给你一个整数数组 nums 和一个整数 k 。 将数组拆分成一些非空子数组。拆分的 代价 是每个子数组中的 重要性 之和。 令 trimmed(subarray) 作为子数组的一个特征,其中所有仅出现一次的数字将会被移除。 例如, trimmed([3,1,2,4,3,4]) = [3,4,3,4]…

category

4

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 数组·哈希·扫描

bolt

答案摘要

我们设计一个函数 ,表示从下标 开始拆分的最小代价。那么答案就是 。 函数 的计算过程如下:

Interview AiBox logo

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

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个整数数组 nums 和一个整数 k

将数组拆分成一些非空子数组。拆分的 代价 是每个子数组中的 重要性 之和。

trimmed(subarray) 作为子数组的一个特征,其中所有仅出现一次的数字将会被移除。

  • 例如,trimmed([3,1,2,4,3,4]) = [3,4,3,4]

子数组的 重要性 定义为 k + trimmed(subarray).length

  • 例如,如果一个子数组是 [1,2,3,3,3,4,4]trimmed([1,2,3,3,3,4,4]) = [3,3,3,4,4] 。这个子数组的重要性就是 k + 5

找出并返回拆分 nums 的所有可行方案中的最小代价。

子数组 是数组的一个连续 非空 元素序列。

 

示例 1:

输入:nums = [1,2,1,2,1,3,3], k = 2
输出:8
解释:将 nums 拆分成两个子数组:[1,2], [1,2,1,3,3]
[1,2] 的重要性是 2 + (0) = 2 。
[1,2,1,3,3] 的重要性是 2 + (2 + 2) = 6 。
拆分的代价是 2 + 6 = 8 ,可以证明这是所有可行的拆分方案中的最小代价。

示例 2:

输入:nums = [1,2,1,2,1], k = 2
输出:6
解释:将 nums 拆分成两个子数组:[1,2], [1,2,1] 。
[1,2] 的重要性是 2 + (0) = 2 。
[1,2,1] 的重要性是 2 + (2) = 4 。
拆分的代价是 2 + 4 = 6 ,可以证明这是所有可行的拆分方案中的最小代价。

示例 3:

输入:nums = [1,2,1,2,1], k = 5
输出:10
解释:将 nums 拆分成一个子数组:[1,2,1,2,1].
[1,2,1,2,1] 的重要性是 5 + (3 + 2) = 10 。
拆分的代价是 10 ,可以证明这是所有可行的拆分方案中的最小代价。

 

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] < nums.length
  • 1 <= k <= 109

 

lightbulb

解题思路

方法一:记忆化搜索

我们设计一个函数 dfs(i)dfs(i),表示从下标 ii 开始拆分的最小代价。那么答案就是 dfs(0)dfs(0)

函数 dfs(i)dfs(i) 的计算过程如下:

  • 如果 ini \ge n,说明已经拆分到了数组末尾,此时返回 00
  • 否则,我们枚举子数组的末尾 jj,过程中用一个数组或哈希表 cnt 统计子数组中每个数字出现的次数,用一个变量 one 统计子数组中出现次数为 11 的数字的个数。那么子数组的重要性就是 k+ji+1onek + j - i + 1 - one,拆分的代价就是 k+ji+1one+dfs(j+1)k + j - i + 1 - one + dfs(j + 1)。我们枚举所有的 jj,取其中的最小值作为 dfs(i)dfs(i) 的返回值。

过程中,我们可以使用记忆化搜索,即使用一个数组 ff 记忆化函数 dfs(i)dfs(i) 的返回值,避免重复计算。

时间复杂度 O(n2)O(n^2),空间复杂度 O(n)O(n)。其中 nn 为数组 numsnums 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
    def minCost(self, nums: List[int], k: int) -> int:
        @cache
        def dfs(i):
            if i >= n:
                return 0
            cnt = Counter()
            one = 0
            ans = inf
            for j in range(i, n):
                cnt[nums[j]] += 1
                if cnt[nums[j]] == 1:
                    one += 1
                elif cnt[nums[j]] == 2:
                    one -= 1
                ans = min(ans, k + j - i + 1 - one + dfs(j + 1))
            return ans

        n = len(nums)
        return dfs(0)
speed

复杂度分析

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

面试官常问的追问

外企场景
  • question_mark

    The candidate should be able to identify the role of dynamic programming in solving the problem efficiently.

  • question_mark

    Look for understanding of how hash table lookups help reduce the cost calculation time.

  • question_mark

    Assess the candidate’s ability to balance the trade-off between array scanning and space/time complexity when partitioning.

warning

常见陷阱

外企场景
  • error

    Incorrectly computing the importance values due to failing to remove unique elements first.

  • error

    Misunderstanding dynamic programming transitions, leading to inefficient solutions.

  • error

    Not utilizing hash tables properly, leading to slower lookups and higher time complexity.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Extend the problem to support multiple ways of splitting the array with different cost constraints.

  • arrow_right_alt

    Consider optimizing the approach for arrays with very large lengths or extreme k values.

  • arrow_right_alt

    Modify the problem to use different subarray importance calculation methods, such as weighted sums.

help

常见问题

外企场景

拆分数组的最小代价题解:数组·哈希·扫描 | LeetCode #2547 困难