Leetcode 3500. Minimum Cost to Divide Array Into Subarrays
题目大意
给定两个等长数组 nums
和 cost
,以及一个整数 k
,你可以将 nums
分割成若干个非空连续子数组。第 i
段子数组(编号从 1 开始)为 nums[l..r]
,其代价计算方式如下:
目标是通过某种划分方式,使得总代价最小。
约束条件
- \(1 \leq n \leq 1000\)
- \(1 \leq nums[i], cost[i], k \leq 1000\)
直观的解法:三重循环暴力 DP
我们定义状态:
dp[i][j]
表示前j
个元素分成i
段的最小总代价。
转移方程为:
即使使用前缀和将单次 \(f(l+1, r, i)\) 的计算复杂度优化到 \(O(1)\),每次仍需枚举 \(n\) 次转移,总体时间复杂度仍为 \(O(n^3)\),无法通过本题。
Abel 求和公式优化
Abel 求和公式简介
Abel 求和公式是一种优化形如 \(\sum a_i b_i\) 的乘积和的技巧,适用于以下场景:
- \(a_i\) 是单调或线性变化
- \(b_i\) 可以预处理为前缀和
当然,根据交换率,\(a_i\) 和 \(b_i\) 可以互换
公式如下:
其中:
- \(B_i = \sum_{j=1}^i a_j\),是从 \(b_1\) 开始的前缀和;
- \(B_{0} = 0\)。
几何意义与解释
可以将 \(\sum a_i b_i\) 理解为“宽度为 \(a_i\),高度为 \(b_i\) 的矩形”所构成的总面积。Abel 求和的思想,相当于用一套更容易累加的方式来表达这些面积之和。
(图源:Easymath-wiki)
加法形式变体(对称结构)
还有一种对称的加法表达:
此时,有\(a_0 = 0\)和\(B_0 = 0\)。
在 Leetcode 3500 中的应用
我们再看代价函数:
将其拆为两部分:
与数组值相关的部分:
用前缀和即可在 \(O(1)\) 时间内得到 \(g(l, r)\)。
与子数组编号 \(i\) 相关的部分:
这类“线性编号 × 区间和”的结构,正是 Abel 求和思想的典型应用场景。我们可以通过Abel求和将其转化为如下形式:
注:\(h'\)函数中计算的是
cost
数组的后辍和。并非\(h\)函数中的\((l, r)\)区间和。
这样一来,\(h'(l, r, i)\)只与变量\(r\)相关,进而减少了DP的状态空间。
时间复杂度优化
通过将 \(f(l, r, i)\) 拆解,并借助前缀和 + Abel 求和思想,我们将原本 \(O(n^3)\) 的三重循环 DP 优化为:
- 单次状态转移:\(O(1)\)
- 空间复杂度:\(O(n)\)
- 总体复杂度:\(O(n^2)\)
可以通过本题的数据范围限制。
相关问题:Codeforces 1175D - Array Splitting
题目链接:Codeforces 原题 | 解答代码 | 官方题解
题目描述
给定数组 \(a = [a_1, ..., a_n]\) 和整数 \(k\),将其划分为 \(k\) 个非空连续子数组,设每个子数组编号从 1 到 \(k\) 递增,总代价函数为:
\(Sub_{i}\) 为第\(i\)个的子数组元素之和。
你需要设计一种划分方式,使得总代价最大。
样例:
设
若划分为三段:\(([1, -2, -3], [4, -5], [6, -7])\),代价为:
Abel 求和视角分析
总代价可以计为:
即每段的区间和乘以其编号 \(i\):
使用Abel求和公式,我们可以进行如下的转化:
我们可以先将所有元素视为编号为 1 的一段:
每新增一段,就意味着将某段的编号从 \(i\) 提升到 \(i+1\),对应的提升值就是“该段和 × 增量编号”。
因此,我们只需选择 \(k-1\) 个切割点,使得后缀和最大的 \(k-1\) 段被额外计入一次,即可达到最大化目标。
实现步骤:
- 枚举每个位置的后缀和(不含第一个);
- 取最大的 \(k-1\) 个后缀和;
- 答案为整段和 + 它们之和。
小结
Abel 求和是一种处理“数列乘编号”类结构的经典技巧,虽然起源于数学,但在很多竞赛问题中都能找到它的影子。特别是在涉及“编号 × 权重”、“位置 × 值”这种结构时,它常常提供一种结构性的优化方式。
在 Leetcode 3500 中,它帮助我们把三重循环的 DP 优化为 \(O(n^2)\);在 CF1175D 中,它转化为一个“最大权重选取”问题。虽然应用方式不同,但核心思想是一样的。
Comments
comments powered by Disqus