区间动态规划例题:石子合并的最小化最终方差问题
题目描述
给定一个长度为 \(n\) 的数组 \(stones\),表示一排石子堆,每堆石子的重量为 \(stones[i]\)。你可以进行 \(n-1\) 次合并操作,每次合并相邻的两堆石子,合并的成本为这两堆石子的重量之和,合并后的新堆重量为两堆重量之和。目标是:经过所有合并操作后,只剩下一堆石子,最小化最终这堆石子的重量的方差(即最小化最终重量与某个目标值 \(T\) 的差的平方,但这里 \(T\) \) 是未知的,我们需要通过合并顺序来最小化最终重量的方差)。但题目可以简化表述为:
给定数组 \(stones\),合并相邻石子堆,每次合并成本为两堆重量和,最终得到一堆总重量为 \(S = \sum stones[i]\) 的石子。我们需要最小化合并过程中所有中间合并产生的“成本”的方差吗?不是,仔细澄清一下。
更准确的定义如下:
设合并过程中,每次合并时记录合并的两堆重量之和(即合并成本)。经过 \(n-1\) 次合并后,会得到一个长度为 \(n-1\) 的成本序列 \(c_1, c_2, \dots, c_{n-1}\)。最终总成本(即总耗费)是固定的,等于 \(S \times (n-1)\) 吗?不对,总成本是 \(\sum c_i\),而这个总成本实际上等于 \(S \times (n-1)\) 吗?我们需要推导。
实际上,每次合并的成本是合并的两堆重量之和,而每次合并后,新堆的重量会被后续合并多次计算。一个经典结论是:在相邻石子合并问题中,总合并成本 = 每堆石子的原始重量 × 该堆石子参与合并的次数。而这个“参与次数”取决于它被合并的时机。因此,总成本是固定的吗?不是,不同的合并顺序会导致不同的总成本。等等,这与我们熟知的“每次合并成本=两堆之和”的问题不同,经典问题是求最小总成本,而这里是求最小化最终重量的方差。让我们重新明确:
最终重量方差 的定义是:设最终合并得到的一堆石子重量为 \(W\)(显然 \(W = S\) 是固定的,因为不管怎么合并,总重量不会变),但方差是对“最终重量”而言的,而最终重量是固定的 \(S\),因此方差为0,这没有意义。
所以这里“方差”实际上是指合并过程中每一步合并得到的新堆重量的方差。即,每次合并后产生一个新堆,其重量是合并的两堆之和,我们需要记录这个新堆的重量,最终得到一个重量序列(长度为 \(n-1\)),然后计算这个序列的方差,并最小化这个方差。
但这样也不常见,常见的变形是:最小化合并过程中产生的最大合并成本(即最小化每一步合并的两堆之和的最大值),这可以用区间DP。但我们这里要求的是最小化合并成本序列的方差,即让合并过程尽量平稳,避免某次合并突然很大或很小。
为了让题目可解,我们明确为:
给定 \(stones[1..n]\),合并相邻石子堆,每次合并的成本 = 两堆重量之和,合并后新堆重量 = 两堆之和。记录每次合并的成本,得到一个成本序列 \(c_1, c_2, \dots, c_{n-1}\)。要最小化这个成本序列的方差,即
\[ > \text{方差} = \frac{1}{n-1} \sum_{i=1}^{n-1} (c_i - \bar{c})^2 > \]
其中 \(\bar{c}\) 是成本序列的平均值。注意 \(\sum c_i\) 并不是常数,因为不同的合并顺序会得到不同的总成本。
解题思路
方差 = \(E(c^2) - (E(c))^2\)。
设总成本 \(T = \sum c_i\),平均值 \(\bar{c} = T/(n-1)\)。
方差 = \(\frac{1}{n-1} \sum c_i^2 - \frac{T^2}{(n-1)^2}\)。
因为 \(n\) 固定,所以最小化方差等价于最小化 \(\sum c_i^2\),因为 \(T^2\) 是 \(T\) 的函数,而 \(T\) 是变化的,不能单独处理。但 \(T\) 实际上等于 \(\sum_{i=1}^n stones[i] \times (\text{该堆参与合并的次数})\),而“参与次数”等于该堆所在的合并区间在合并树中的深度。经典结论:总成本 \(T = \sum_{i=1}^n w_i \times (\text{该堆被合并的次数})\),且次数 = 该堆在合并树中到根的距离(叶子的深度)。不同的合并顺序对应不同的二叉树结构,叶子的深度不同,所以 \(T\) 可变。
因此,最小化方差需要同时考虑 \(\sum c_i^2\) 和 \(T^2\),不能简单化为只最小化平方和。
但我们可以用DP状态记录 \(T\) 和 \(\sum c_i^2\) 吗?复杂度太高。通常这类“最小化方差”问题,在区间DP中,我们可以用最小化每一步的平方和作为目标,因为方差与平方和、总和有关。但这里总成本 \(T\) 会变化,所以不能直接只最小化平方和。
不过,一个经典技巧是:方差最小化等价于最小化平方和,当平均值固定时。但这里平均值不固定,所以我们需要枚举可能的平均值?不可行。
另一种方法:方差 = \(\min_{a} \sum (c_i - a)^2 / (n-1)\),对固定的序列,当 \(a = \bar{c}\) 时取最小。所以我们的目标 = \(\min_{\text{合并顺序}} \min_{a} \sum (c_i - a)^2\)。交换次序:\(\min_{a} \min_{\text{合并顺序}} \sum (c_i - a)^2\)。对固定的 \(a\),我们可以定义一个新的成本:每次合并成本为两堆之和 \(c\),但贡献到目标函数的是 \((c-a)^2\)。这样就是一个新的区间DP问题:合并相邻石子,每次合并的代价为 \((c-a)^2\),求最小总代价。然后外层枚举 \(a\) 找到最小总代价。
但 \(a\) 是实数,不可枚举所有。注意到 \(c\) 是整数(石子重量整数),\(a\) 可以离散化到所有可能的合并成本值附近。但合并成本是连续的吗?不,合并成本是石子重量的和,是整数。所以 \(a\) 在整数附近枚举可能的最优值。但 \(a\) 的取值可能很多。更实际的是:我们观察到方差是凸函数,可以三分搜索 \(a\) 来找到最小方差。
算法步骤:
- 计算前缀和 \(prefix\),方便快速得到区间和。
- 对于给定的参数 \(a\),定义 \(dp[i][j]\) 表示合并区间 \([i, j]\) 的石子成一堆时,最小的 \(\sum (c_k - a)^2\) 值,其中 \(c_k\) 是这个区间合并过程中每次合并的成本。
- 转移:最后一次合并是将 \([i, k]\) 和 \([k+1, j]\) 两堆合并,成本为 \(sum(i,j)\)(区间和)。在此之前,左区间合并的代价和为 \(dp[i][k]\),右区间为 \(dp[k+1][j]\)。所以:
\[ dp[i][j] = \min_{i \le k < j} \{ dp[i][k] + dp[k+1][j] + (sum(i,j) - a)^2 \} \]
初始化:当 \(i=j\) 时,区间只有一堆,不需要合并,所以合并成本序列为空,\(dp[i][i] = 0\)。
4. 最终答案 = \(dp[1][n]\) 对应这个 \(a\) 的最小平方偏差和。方差 = 这个值除以 \((n-1)\)。
5. 外层三分搜索 \(a\) 在某个范围(如 \([0, S]\)),找到最小的 \(dp[1][n]\),然后得到最小方差。
细节与解释
-
为什么可以这样定义 \(dp\)?
因为合并区间 \([i,j]\) 时,无论内部如何合并,最终这堆重量一定是 \(sum(i,j)\),且合并过程形成一棵二叉树,叶子的合并成本是树的内部节点的权值(左右子重量和)。这棵树的每个内部节点的权值为左右子树重量和,我们的目标是让这些节点权值与 \(a\) 的差的平方和最小。这正好是树形DP,但可以化作区间DP,因为合并顺序对应二叉树的结构,可以用区间DP枚举最后一次合并的分割点。 -
时间复杂度:
区间DP本身 \(O(n^3)\),外层三分搜索 \(a\) 需要 \(O(\log (S/\epsilon))\) 次迭代,\(S\) 是总重量。所以总复杂度 \(O(n^3 \log S)\),在 \(n \le 100\) 时可行。 -
边界:
\(a\) 的搜索范围是 \([0, S]\),因为每次合并成本至少是两堆最小和,最多是 \(S\),但实际 \(a\) 可能在平均值附近,取 \([0, S]\) 安全。 -
实现注意:
DP 时用前缀和快速计算 \(sum(i,j)\)。三分搜索时比较 \(dp\) 值,取使 \(dp\) 最小的 \(a\)。
举例说明
设 \(stones = [1, 2, 3]\):
总重量 \(S=6\),\(n=3\) 需要合并2次。
搜索 \(a\) 从0到6,比如 \(a=3\):
- 区间[1,1]:dp=0,[2,2]:0,[3,3]:0。
- 区间[1,2]:合并成本=1+2=3,平方偏差=(3-3)^2=0,dp=0+0+0=0。
- 区间[2,3]:合并成本=5,平方偏差=4,dp=0+0+4=4。
- 区间[1,3]:枚举k=1时,左[1,1]右[2,3],合并成本=6,平方偏差=9,总=0+4+9=13;k=2时,左[1,2]右[3,3],合并成本=6,平方偏差=9,总=0+0+9=9。取最小9。
所以 \(a=3\) 时总平方偏差=9,方差=9/2=4.5。
尝试其他 \(a\) 找到更小的总平方偏差。
总结
这个问题的核心是:
- 将最小化方差转化为对参数 \(a\) 的最小化平方偏差和问题。
- 对每个 \(a\),用区间DP计算最小平方偏差和。
- 用三分搜索找到最优的 \(a\)。
这样就能求出合并顺序,使得合并成本序列的方差最小,从而使合并过程更加平稳。