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

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

我将为你讲解一个关于石子合并的区间动态规划问题。这个问题是经典石子合并问题的一个变体,其中合并两堆石子的得分不是简单的两堆石子数之和,而是其和的立方。这会导致状态转移时,合并得分不再是简单的加法,而是需要计算立方值,从而影响最优决策。

问题描述

我们有 N 堆石子排成一行,每堆石子有一定的数量(正整数)。我们希望通过一系列合并操作,将所有石子合并为一堆。每次合并操作只能合并相邻的两堆石子,合并的得分等于这两堆石子的数量之和的立方。目标是找到一种合并顺序,使得总得分最小(即最小化所有合并操作的得分总和)。

例如,石子堆的数量数组为 [1, 2, 3]。一种可能的合并顺序是:

  1. 合并前两堆 (1+2)³ = 27,剩下 [3, 3]。
  2. 合并剩下的两堆 (3+3)³ = 216,总得分 27+216=243。
    但我们希望找到总得分最小的合并顺序。

解题思路

这是一个典型的区间动态规划问题,我们可以定义状态 dp[l][r] 表示将第 l 堆到第 r 堆石子(闭区间)合并为一堆的最小总得分。我们的最终目标是 dp[1][N]

由于每次合并相邻两堆,我们可以考虑最后一次合并发生在位置 k,即先将区间 [l, k] 合并为一堆,再将区间 [k+1, r] 合并为一堆,然后合并这两堆。因此,状态转移方程的核心思想是:

dp[l][r] = min{ dp[l][k] + dp[k+1][r] + (sum(l,k) + sum(k+1,r))³ } 对于所有 k ∈ [l, r-1]

这里 sum(l, r) 表示区间 [l, r] 内石子的总数量。由于合并成两堆后,这两堆的数量分别是 sum(l, k)sum(k+1, r),最后合并这两堆的得分就是它们和的立方。

我们可以预处理前缀和数组 prefix,使得 sum(l, r) = prefix[r] - prefix[l-1],这样可以在 O(1) 时间内计算区间和。

详细步骤

  1. 定义状态

    • dp[i][j] 表示合并第 i 堆到第 j 堆石子(1 ≤ i ≤ j ≤ N)的最小总得分。
  2. 初始化

    • 当区间长度为 1 时(即 i == j),不需要合并,得分为 0。所以 dp[i][i] = 0
  3. 状态转移

    • 对于区间 [l, r],我们枚举分割点 k(l ≤ k < r),表示最后一步合并是将 [l, k] 合并成的一堆与 [k+1, r] 合并成的一堆进行合并。
    • 左区间合并的最小得分为 dp[l][k],右区间为 dp[k+1][r]
    • 最后合并的得分为 (sum(l,k) + sum(k+1,r))³,即 (sum(l, r))³。注意,实际上左堆数量为 sum(l,k),右堆为 sum(k+1,r),它们的和就是整个区间的总石子数 sum(l, r)
    • 所以转移方程为:
      dp[l][r] = min{ dp[l][k] + dp[k+1][r] } + (sum(l, r))³
      其中 k 从 l 遍历到 r-1。
      
    • 这里关键点是:无论怎么分割,最后一步的合并得分只取决于整个区间的总石子数,与分割点 k 无关。但不同的 k 会影响 dp[l][k] + dp[k+1][r] 的值,我们需要选择最小的那个。
  4. 计算顺序

    • 区间 DP 通常按区间长度从小到大计算。先计算所有长度为 2 的区间,然后长度 3,直到长度 N。
  5. 答案

    • 最终答案是 dp[1][N]

为什么立方会导致不同的最优解?

在经典的石子合并问题中(得分是两堆石子数之和),最优合并顺序通常与数组顺序有关,但得分函数是线性的。而这里得分是和的立方,这意味着合并两个大堆的代价会非常高(因为立方增长很快)。因此,算法会倾向于尽早合并较小的堆,以避免在最后合并两个大堆时产生巨大的立方代价。状态转移中的 min 操作会自动寻找这种策略。

示例演示

假设石子堆为 [1, 2, 3]。

  • 前缀和: prefix[0]=0, prefix[1]=1, prefix[2]=3, prefix[3]=6。
  • 区间长度 1: dp[1][1]=0, dp[2][2]=0, dp[3][3]=0。
  • 长度 2:
    • dp[1][2]: k=1, 左堆 sum(1,1)=1, 右堆 sum(2,2)=2, 总 sum=3, 得分=3³=27。dp[1][2] = 0+0+27 = 27。
    • dp[2][3]: k=2, 总 sum=2+3=5, 得分=125。dp[2][3] = 0+0+125 = 125。
  • 长度 3: dp[1][3]
    • k=1: 左堆 sum(1,1)=1, 右堆 sum(2,3)=5, 总 sum=6, 得分=216。dp[1][1]+dp[2][3]+216 = 0+125+216 = 341。
    • k=2: 左堆 sum(1,2)=3, 右堆 sum(3,3)=3, 总 sum=6, 得分=216。dp[1][2]+dp[3][3]+216 = 27+0+216 = 243。
    • 取最小值 243。
      所以最小总得分为 243,与之前例子一致(对应合并顺序:先合并前两堆,再合并结果与第三堆)。

复杂度分析

  • 状态数 O(N²)。
  • 每个状态需要枚举分割点 k,O(N)。
  • 总时间复杂度 O(N³),空间复杂度 O(N²)(可以使用滚动数组优化空间,但这里不必要)。

核心要点

  • 区间 DP 是解决合并类问题的标准方法。
  • 状态定义要清晰:dp[l][r] 表示合并该区间的最小总得分。
  • 状态转移时,注意最后一步合并的得分计算:由于是两堆合并,得分取决于这两堆石子数的和,而这两堆正好是左右子区间的总石子数,所以最终合并得分就是整个区间总石子数的立方。
  • 由于立方函数增长迅速,算法会自然倾向于让大堆尽可能晚合并(但实际上我们求最小值,所以会尽可能让大堆“晚”形成,从而减少立方代价)。

这个问题的变体(比如得分是平方、四次方等)都可以用类似方法解决,只需修改最后合并时的代价函数即可。

合并相邻石子堆的最小总成本(每次合并得分等于两堆石子数之和的立方) 我将为你讲解一个关于石子合并的区间动态规划问题。这个问题是经典石子合并问题的一个变体,其中合并两堆石子的得分不是简单的两堆石子数之和,而是其和的 立方 。这会导致状态转移时,合并得分不再是简单的加法,而是需要计算立方值,从而影响最优决策。 问题描述 我们有 N 堆石子排成一行,每堆石子有一定的数量(正整数)。我们希望通过一系列合并操作,将所有石子合并为一堆。每次合并操作只能合并 相邻 的两堆石子,合并的 得分 等于这两堆石子的数量之和的 立方 。目标是找到一种合并顺序,使得 总得分最小 (即最小化所有合并操作的得分总和)。 例如,石子堆的数量数组为 [ 1, 2, 3 ]。一种可能的合并顺序是: 合并前两堆 (1+2)³ = 27,剩下 [ 3, 3 ]。 合并剩下的两堆 (3+3)³ = 216,总得分 27+216=243。 但我们希望找到总得分最小的合并顺序。 解题思路 这是一个典型的区间动态规划问题,我们可以定义状态 dp[l][r] 表示将第 l 堆到第 r 堆石子(闭区间)合并为一堆的 最小总得分 。我们的最终目标是 dp[1][N] 。 由于每次合并相邻两堆,我们可以考虑最后一次合并发生在位置 k,即先将区间 [ l, k] 合并为一堆,再将区间 [ k+1, r ] 合并为一堆,然后合并这两堆。因此,状态转移方程的核心思想是: 这里 sum(l, r) 表示区间 [ l, r] 内石子的总数量。由于合并成两堆后,这两堆的数量分别是 sum(l, k) 和 sum(k+1, r) ,最后合并这两堆的得分就是它们和的立方。 我们可以预处理前缀和数组 prefix ,使得 sum(l, r) = prefix[r] - prefix[l-1] ,这样可以在 O(1) 时间内计算区间和。 详细步骤 定义状态 : 设 dp[i][j] 表示合并第 i 堆到第 j 堆石子(1 ≤ i ≤ j ≤ N)的最小总得分。 初始化 : 当区间长度为 1 时(即 i == j),不需要合并,得分为 0。所以 dp[i][i] = 0 。 状态转移 : 对于区间 [ l, r],我们枚举分割点 k(l ≤ k < r),表示最后一步合并是将 [ l, k] 合并成的一堆与 [ k+1, r ] 合并成的一堆进行合并。 左区间合并的最小得分为 dp[l][k] ,右区间为 dp[k+1][r] 。 最后合并的得分为 (sum(l,k) + sum(k+1,r))³ ,即 (sum(l, r))³ 。注意,实际上左堆数量为 sum(l,k) ,右堆为 sum(k+1,r) ,它们的和就是整个区间的总石子数 sum(l, r) 。 所以转移方程为: 这里关键点是:无论怎么分割,最后一步的合并得分只取决于整个区间的总石子数,与分割点 k 无关。但不同的 k 会影响 dp[l][k] + dp[k+1][r] 的值,我们需要选择最小的那个。 计算顺序 : 区间 DP 通常按区间长度从小到大计算。先计算所有长度为 2 的区间,然后长度 3,直到长度 N。 答案 : 最终答案是 dp[1][N] 。 为什么立方会导致不同的最优解? 在经典的石子合并问题中(得分是两堆石子数之和),最优合并顺序通常与数组顺序有关,但得分函数是线性的。而这里得分是和的立方,这意味着合并两个大堆的代价会非常高(因为立方增长很快)。因此,算法会倾向于 尽早合并较小的堆 ,以避免在最后合并两个大堆时产生巨大的立方代价。状态转移中的 min 操作会自动寻找这种策略。 示例演示 假设石子堆为 [ 1, 2, 3 ]。 前缀和: prefix[ 0]=0, prefix[ 1]=1, prefix[ 2]=3, prefix[ 3 ]=6。 区间长度 1: dp[ 1][ 1]=0, dp[ 2][ 2]=0, dp[ 3][ 3 ]=0。 长度 2: dp[ 1][ 2]: k=1, 左堆 sum(1,1)=1, 右堆 sum(2,2)=2, 总 sum=3, 得分=3³=27。dp[ 1][ 2 ] = 0+0+27 = 27。 dp[ 2][ 3]: k=2, 总 sum=2+3=5, 得分=125。dp[ 2][ 3 ] = 0+0+125 = 125。 长度 3: dp[ 1][ 3 ] k=1: 左堆 sum(1,1)=1, 右堆 sum(2,3)=5, 总 sum=6, 得分=216。dp[ 1][ 1]+dp[ 2][ 3 ]+216 = 0+125+216 = 341。 k=2: 左堆 sum(1,2)=3, 右堆 sum(3,3)=3, 总 sum=6, 得分=216。dp[ 1][ 2]+dp[ 3][ 3 ]+216 = 27+0+216 = 243。 取最小值 243。 所以最小总得分为 243,与之前例子一致(对应合并顺序:先合并前两堆,再合并结果与第三堆)。 复杂度分析 状态数 O(N²)。 每个状态需要枚举分割点 k,O(N)。 总时间复杂度 O(N³),空间复杂度 O(N²)(可以使用滚动数组优化空间,但这里不必要)。 核心要点 区间 DP 是解决合并类问题的标准方法。 状态定义要清晰: dp[l][r] 表示合并该区间的最小总得分。 状态转移时,注意最后一步合并的得分计算:由于是两堆合并,得分取决于这两堆石子数的和,而这两堆正好是左右子区间的总石子数,所以最终合并得分就是整个区间总石子数的立方。 由于立方函数增长迅速,算法会自然倾向于让大堆尽可能晚合并(但实际上我们求最小值,所以会尽可能让大堆“晚”形成,从而减少立方代价)。 这个问题的变体(比如得分是平方、四次方等)都可以用类似方法解决,只需修改最后合并时的代价函数即可。