合并石子堆的最小总成本(每次合并相邻两堆,得分等于两堆石子数之和的立方)
字数 1948 2025-12-17 20:10:37

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


题目描述

n 堆石子排成一列,第 i 堆有 stones[i] 个石子。每次只能合并相邻的两堆,合并的得分等于这两堆石子的数量之和的立方(即 (stones[i] + stones[j])^3),合并后新堆的石子数为两堆之和,位置在原来两堆的区间处。求将所有石子合并成一堆的最小总得分

示例

输入: stones = [1, 2, 3]
输出: 28
解释: 
一种最优合并方式:
1. 合并 [1, 2] -> 新堆 3,得分 (1+2)^3 = 27,数组变为 [3, 3]
2. 合并 [3, 3] -> 新堆 6,得分 (3+3)^3 = 216
总得分 = 27 + 216 = 243?等等,这里计算有误。我们重新算一下。

我们先验证例子:
初始:[1,2,3]

  • 合并(1,2):得分 (3)^3=27,数组→[3,3]
  • 合并(3,3):得分 (6)^3=216,总得分=27+216=243
    但题目要求最小总得分,可能存在更小的合并方式:
  • 合并(2,3):得分 (5)^3=125,数组→[1,5]
  • 合并(1,5):得分 (6)^3=216,总得分=125+216=341(更大)
    看来这个例子中第一种方式更小。

但注意,本题的得分计算是立方而不是平方,因此合并顺序会显著影响总得分,因为立方会放大石子数量多的堆的影响。我们需要找到最小的总得分,而不是最大。


解题思路(区间DP)

这个问题是经典“石子合并”问题的变体,只是得分计算从“两堆石子数量之和”的平方变成了立方
区间DP适合解决这种“相邻合并”问题,因为每次合并会合并一个连续区间。

1. 状态定义

  • dp[l][r] 表示将区间 [l, r] 的所有石子合并成一堆的最小总得分(l, r 从 0 到 n-1)。
  • prefix[i] 表示前 i 堆石子的前缀和,用于快速计算区间和。

定义 sum(l, r) = prefix[r+1] - prefix[l] 为区间 [l, r] 的石子总数。

最终目标dp[0][n-1]


2. 状态转移

考虑最后一次合并的位置:
在区间 [l, r] 中,最后一步是将 [l, k][k+1, r] 两堆合并(其中 l ≤ k < r)。
合并前,左边一堆的总石子数 = sum(l, k),右边一堆的总石子数 = sum(k+1, r)
合并这两堆的得分 = (sum(l, k) + sum(k+1, r))^3,而 sum(l, k) + sum(k+1, r) = sum(l, r),所以最后一步得分固定为 (sum(l, r))^3,与 k 无关。

但 k 的选择会影响之前合并的得分:
之前将 [l, k] 合并成一堆的最小得分是 dp[l][k],将 [k+1, r] 合并成一堆的最小得分是 dp[k+1][r]

因此:

dp[l][r] = min_{k = l .. r-1} ( dp[l][k] + dp[k+1][r] ) + (sum(l, r))^3

注意:当 l == r 时,只有一堆,不需要合并,得分为 0。


3. 计算顺序

因为区间长度从短到长计算,所以按区间长度 len 从 2 到 n 枚举。
先计算 sum(l, r) 可以用前缀和 O(1) 得到。


4. 边界条件

  • l == r 时,dp[l][r] = 0

5. 举例说明

stones = [1, 2, 3]
n = 3
前缀和 prefix = [0, 1, 3, 6]

len = 2:

  • l=0,r=1: sum=3, 立方=27,只有 k=0 一种分割:dp[0][0]+dp[1][1]+27 = 0+0+27 = 27
  • l=1,r=2: sum=5, 立方=125,dp[1][2]=125

len = 3: l=0,r=2, sum=6, 立方=216
k=0: dp[0][0]+dp[1][2]+216 = 0+125+216 = 341
k=1: dp[0][1]+dp[2][2]+216 = 27+0+216 = 243
取 min = 243

所以 dp[0][2] = 243,这就是最小总得分。
与之前手动比较一致,第一种合并方式得 243,第二种得 341,所以最小是 243。


6. 复杂度

状态数 O(n²),每个状态需要枚举分割点 k,O(n) 个,总 O(n³)。
对于 n 较小(如 ≤ 200)可用。


代码实现(Python 示例)

def min_cost_merge_cubic(stones):
    n = len(stones)
    prefix = [0] * (n + 1)
    for i in range(n):
        prefix[i + 1] = prefix[i] + stones[i]
    
    def sum_range(l, r):
        return prefix[r + 1] - prefix[l]
    
    dp = [[0] * n for _ in range(n)]
    for length in range(2, n + 1):  # 区间长度
        for l in range(n - length + 1):
            r = l + length - 1
            dp[l][r] = float('inf')
            total = sum_range(l, r)
            merge_cost = total ** 3
            for k in range(l, r):
                dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + merge_cost)
    return dp[0][n - 1]

# 测试
stones = [1, 2, 3]
print(min_cost_merge_cubic(stones))  # 输出 243

思考与扩展

  1. 如果是平方,就是经典石子合并,可以用四边形不等式优化到 O(n²),但这里是立方,仍然可以这样优化吗?
    在平方情况下,代价满足四边形不等式,但立方不满足,所以不能直接用四边形不等式优化。
  2. 如果得分改为和的一次方,总得分固定,与合并顺序无关,因为每次合并得分=两堆之和,总得分=∑(每石子参与合并次数)。

这样,你理解了“合并石子堆的立方得分最小化”的区间 DP 解法吗?

合并石子堆的最小总成本(每次合并相邻两堆,得分等于两堆石子数之和的立方) 题目描述 有 n 堆石子排成一列,第 i 堆有 stones[i] 个石子。每次只能合并 相邻 的两堆,合并的 得分 等于这两堆石子的数量之和的 立方 (即 (stones[i] + stones[j])^3 ),合并后新堆的石子数为两堆之和,位置在原来两堆的区间处。求将所有石子合并成一堆的 最小总得分 。 示例 我们先验证例子: 初始:[ 1,2,3 ] 合并(1,2):得分 (3)^3=27,数组→[ 3,3 ] 合并(3,3):得分 (6)^3=216,总得分=27+216=243 但题目要求 最小总得分 ,可能存在更小的合并方式: 合并(2,3):得分 (5)^3=125,数组→[ 1,5 ] 合并(1,5):得分 (6)^3=216,总得分=125+216=341(更大) 看来这个例子中第一种方式更小。 但注意,本题的得分计算是 立方 而不是平方,因此合并顺序会显著影响总得分,因为立方会放大石子数量多的堆的影响。我们需要找到最小的总得分,而不是最大。 解题思路(区间DP) 这个问题是经典“石子合并”问题的变体,只是得分计算从“两堆石子数量之和”的 平方 变成了 立方 。 区间DP适合解决这种“相邻合并”问题,因为每次合并会合并一个连续区间。 1. 状态定义 设 dp[l][r] 表示将区间 [l, r] 的所有石子合并成一堆的 最小总得分 (l, r 从 0 到 n-1)。 prefix[i] 表示前 i 堆石子的前缀和,用于快速计算区间和。 定义 sum(l, r) = prefix[r+1] - prefix[l] 为区间 [l, r] 的石子总数。 最终目标 : dp[0][n-1] 。 2. 状态转移 考虑最后一次合并的位置: 在区间 [l, r] 中,最后一步是将 [l, k] 和 [k+1, r] 两堆合并(其中 l ≤ k < r )。 合并前,左边一堆的总石子数 = sum(l, k) ,右边一堆的总石子数 = sum(k+1, r) 。 合并这两堆的得分 = (sum(l, k) + sum(k+1, r))^3 ,而 sum(l, k) + sum(k+1, r) = sum(l, r) ,所以 最后一步得分固定为 (sum(l, r))^3 ,与 k 无关。 但 k 的选择会影响之前合并的得分: 之前将 [l, k] 合并成一堆的最小得分是 dp[l][k] ,将 [k+1, r] 合并成一堆的最小得分是 dp[k+1][r] 。 因此: 注意 :当 l == r 时,只有一堆,不需要合并,得分为 0。 3. 计算顺序 因为区间长度从短到长计算,所以按区间长度 len 从 2 到 n 枚举。 先计算 sum(l, r) 可以用前缀和 O(1) 得到。 4. 边界条件 当 l == r 时, dp[l][r] = 0 。 5. 举例说明 stones = [ 1, 2, 3 ] n = 3 前缀和 prefix = [ 0, 1, 3, 6 ] len = 2: l=0,r=1: sum=3, 立方=27,只有 k=0 一种分割:dp[ 0][ 0]+dp[ 1][ 1 ]+27 = 0+0+27 = 27 l=1,r=2: sum=5, 立方=125,dp[ 1][ 2 ]=125 len = 3: l=0,r=2, sum=6, 立方=216 k=0: dp[ 0][ 0]+dp[ 1][ 2 ]+216 = 0+125+216 = 341 k=1: dp[ 0][ 1]+dp[ 2][ 2 ]+216 = 27+0+216 = 243 取 min = 243 所以 dp[0][2] = 243 ,这就是最小总得分。 与之前手动比较一致,第一种合并方式得 243,第二种得 341,所以最小是 243。 6. 复杂度 状态数 O(n²),每个状态需要枚举分割点 k,O(n) 个,总 O(n³)。 对于 n 较小(如 ≤ 200)可用。 代码实现(Python 示例) 思考与扩展 如果是平方,就是经典石子合并,可以用四边形不等式优化到 O(n²),但这里是立方,仍然可以这样优化吗? 在平方情况下,代价满足四边形不等式,但立方不满足,所以不能直接用四边形不等式优化。 如果得分改为和的一次方,总得分固定,与合并顺序无关,因为每次合并得分=两堆之和,总得分=∑(每石子参与合并次数)。 这样,你理解了“合并石子堆的立方得分最小化”的区间 DP 解法吗?