合并相邻石子堆的最小总成本(每次合并得分等于两堆石子数之和的立方)
字数 333 2025-12-18 15:58:36

合并相邻石子堆的最小总成本(每次合并得分等于两堆石子数之和的立方)


题目描述

你有一排 n 堆石子排成一行,第 i 堆石子的数量为 stones[i]stones[i] > 0)。每次操作你可以选择相邻的两堆石子,将它们合并为一堆,合并的成本(或得分)为这两堆石子数量之和的立方(即 (stones[i] + stones[i+1])^3)。你需要进行 n-1 次合并操作,直到所有石子合并成一堆。请设计算法,计算将这一排石子合并为一堆的最小总成本(即最小总得分)。

示例:

输入:stones = [1, 2, 3, 4]
输出:170
解释:
一种合并顺序:
1. 合并 (1, 2) -> 新堆为 [3, 3, 4],成本 = (1+2)^3 = 27
2. 合并 (3, 3) -> 新堆为 [6, 4],成本 = (3+3)^3 = 216
3. 合并 (6, 4) -> 新堆为 [10],成本 = (6+4)^3 = 1000
总成本 = 27 + 216 + 1000 = 1243(不是最小)

另一种合并顺序:
1. 合并 (2, 3) -> 新堆为 [1, 5, 4],成本 = (2+3)^3 = 125
2. 合并 (5, 4) -> 新堆为 [1, 9],成本 = (5+4)^3 = 729
3. 合并 (1, 9) -> 新堆为 [10],成本 = (1+9)^3 = 1000
总成本 = 125 + 729 + 1000 = 1854(也不是最小)

可以验证,最小总成本是 170,对应合并顺序:
1. 合并 (3, 4) -> 新堆为 [1, 2, 7],成本 = (3+4)^3 = 343
2. 合并 (1, 2) -> 新堆为 [3, 7],成本 = (1+2)^3 = 27
3. 合并 (3, 7) -> 新堆为 [10],成本 = (3+7)^3 = 1000
总成本 = 343 + 27 + 1000 = 1370?等等,这里计算有误,343+27=370,370+1000=1370,不是170。
我们需要重新检查示例。

实际上,对于 stones = [1, 2, 3, 4],正确的最小总成本应该是 170。让我们验证一下:
一种可能:
1. 合并 (1, 2) -> [3, 3, 4],成本 = 27
2. 合并 (3, 4) -> [3, 7],成本 = (3+4)^3 = 343
3. 合并 (3, 7) -> [10],成本 = 1000
总成本 = 27 + 343 + 1000 = 1370(仍然很大)。

显然我对示例的解释有误。让我们直接计算一下:目标是最小化立方和。
由于立方函数增长很快,我们应尽量让每次合并的两堆石子数量之和较小,因此倾向于先合并较小的数。
尝试:
1. 合并 (1, 2) -> [3, 3, 4],成本 = 27
2. 合并 (3, 3) -> [6, 4],成本 = 216
3. 合并 (6, 4) -> [10],成本 = 1000
总成本 = 1243。

另一种:
1. 合并 (3, 4) -> [1, 2, 7],成本 = 343
2. 合并 (1, 2) -> [3, 7],成本 = 27
3. 合并 (3, 7) -> [10],成本 = 1000
总成本 = 1370。

再试:
1. 合并 (2, 3) -> [1, 5, 4],成本 = 125
2. 合并 (1, 5) -> [6, 4],成本 = 216
3. 合并 (6, 4) -> [10],成本 = 1000
总成本 = 1341。

看起来最小值可能很大,不会是 170。
所以示例可能给错了,或者是成本函数不同(比如平方而不是立方)。但题目要求是立方,我们按立方来做。

为了简化理解,我们假设成本函数为 `(a+b)^3`,目标是求最小总成本。
实际正确示例可以是:
stones = [1, 2, 3],最小总成本:
1. 合并 (1, 2) -> [3, 3],成本 = 27
2. 合并 (3, 3) -> [6],成本 = 216
总成本 = 243。
另一种:
1. 合并 (2, 3) -> [1, 5],成本 = 125
2. 合并 (1, 5) -> [6],成本 = 216
总成本 = 341。
所以最小是 243。

题目理解清楚后,我们进入解题过程。

---

**解题过程**

这是一个典型的区间动态规划问题。与经典的“合并石子(最小得分,成本为两堆之和)”类似,但成本函数从线性变成了立方,这改变了最优子结构的性质吗?没有,仍然满足区间DP的特征:最后一次合并一定是将某个前缀区间合并的一堆与后缀区间合并的一堆再进行合并。

**1. 定义状态**
设 `dp[i][j]` 表示将区间 `[i, j]`(1-indexed 或 0-indexed)内的所有石子合并成一堆的**最小总成本**(即从 `i` 到 `j` 这些堆合并成一堆的最小得分和)。

**2. 状态转移**
考虑最后一次合并:区间 `[i, j]` 最终一定是由两个子区间 `[i, k]` 和 `[k+1, j]` 合并而成(其中 `i ≤ k < j`)。那么:
- 先将 `[i, k]` 合并成一堆,成本为 `dp[i][k]`
- 再将 `[k+1, j]` 合并成一堆,成本为 `dp[k+1][j]`
- 最后将这两堆合并,成本为 `(sum(i,k) + sum(k+1,j))^3`,其中 `sum(i,j)` 表示区间 `[i, j]` 的石子总数。
由于 `sum(i,k) + sum(k+1,j) = sum(i,j)`,所以最后一次合并的成本就是 `(sum(i,j))^3`,与 `k` 无关。

因此状态转移方程为:

dp[i][j] = min_{k=i}^{j-1} { dp[i][k] + dp[k+1][j] } + (sum(i,j))^3

其中 `sum(i,j)` 可以用前缀和数组 `prefix` 快速计算:`sum(i,j) = prefix[j] - prefix[i-1]`。

**3. 初始化**
- 当区间长度为 1 时,即 `i == j`,不需要合并,成本为 0:`dp[i][i] = 0`。
- 当区间长度大于 1 时,初始化为一个较大值(比如 `inf`),然后通过枚举分割点 `k` 来更新。

**4. 计算顺序**
区间DP通常按**区间长度**从小到大计算。先算所有长度为 2 的区间,再算长度为 3 的区间,一直到长度为 n 的区间 `[1, n]`。

**5. 最终答案**
最终答案就是 `dp[1][n]`(假设下标从 1 开始)。

---

**6. 时间复杂度**
状态数 O(n²),每个状态需要枚举分割点 O(n),总复杂度 O(n³)。对于 n 较小(比如几百)的问题可以接受。

---

**7. 举例推导**
以 `stones = [1, 2, 3]` 为例(n=3):
前缀和 `prefix = [0, 1, 3, 6]`(下标从 1 开始)。

- 长度为 1:`dp[1][1]=0`, `dp[2][2]=0`, `dp[3][3]=0`
- 长度为 2:
  - `[1,2]`:`sum=3`,只能 `k=1`:`dp[1][1]+dp[2][2]+27 = 0+0+27=27`
  - `[2,3]`:`sum=5`,只能 `k=2`:`dp[2][2]+dp[3][3]+125 = 0+0+125=125`
- 长度为 3:`[1,3]`,`sum=6`
  - `k=1`:`dp[1][1]+dp[2][3]+216 = 0+125+216=341`
  - `k=2`:`dp[1][2]+dp[3][3]+216 = 27+0+216=243`
  - 取 min:`dp[1][3]=243`

最终答案 `dp[1][3]=243`,与我们之前手动计算一致。

---

**8. 总结**
本题是经典区间DP的变种,成本函数为立方后,最后一次合并的成本只取决于区间总和,与分割点无关,但**子区间的最优成本**仍然需要通过枚举分割点来求得最小。因此直接套用区间DP模板即可。
合并相邻石子堆的最小总成本(每次合并得分等于两堆石子数之和的立方) 题目描述 你有一排 n 堆石子排成一行,第 i 堆石子的数量为 stones[i] ( stones[i] > 0 )。每次操作你可以选择 相邻 的两堆石子,将它们合并为一堆,合并的成本(或得分)为这两堆石子数量之和的 立方 (即 (stones[i] + stones[i+1])^3 )。你需要进行 n-1 次合并操作,直到所有石子合并成一堆。请设计算法,计算将这一排石子合并为一堆的 最小总成本 (即最小总得分)。 示例: dp[ i][ j] = min_ {k=i}^{j-1} { dp[ i][ k] + dp[ k+1][ j ] } + (sum(i,j))^3