未被详细、明确地作为独立标题讲述过
字数 4295 2025-12-24 11:34:48

好的,我们来看一个在“区间动态规划”领域中具有代表性,且在您提供的列表中 未被详细、明确地作为独立标题讲述过 的经典问题:

题目:合并相邻石子堆的最小成本问题(每次合并的得分为两堆石子数量之和的平方,求最小总得分)

问题描述

我们有一排 n 堆石子,排成一行。第 i 堆石子有 stones[i] 个石子。我们的目标是将所有石子堆合并成一堆

合并的规则是:每次只能合并相邻的两堆石子。合并的成本(或称为得分)定义为被合并的两堆石子的石子数量之和的平方
例如,合并两堆石子,数量分别为 ab,则本次合并的成本为 (a + b)^2

在整个过程中,你需要不断地合并相邻堆,直到只剩下一堆为止。不同的合并顺序会导致总成本不同。
请你设计一个算法,计算出将所有石子堆合并成一堆的最小总成本

示例
输入:stones = [1, 2, 3]
输出:29
解释:
方案一:先合并前两堆,成本 (1+2)^2 = 9,得到新堆 [3, 3];再合并这两堆,成本 (3+3)^2 = 36;总成本 9 + 36 = 45
方案二:先合并后两堆,成本 (2+3)^2 = 25,得到新堆 [1, 5];再合并这两堆,成本 (1+5)^2 = 36;总成本 25 + 36 = 61
方案三(最优):先合并第1和第3堆?不可以,因为它们不相邻。
先合并1和2得 [3, 3],成本9;再合并得36,总45。
先合并2和3得 [1, 5],成本25;再合并得36,总61。
等等...等一下,似乎没有比45更小的了?让我再算算。

等等,我上面的计算有误。关键在于合并后新堆的石子数是两堆之和,这个和会参与下一次合并
让我们用区间DP来正确计算:

对于 [1, 2, 3]:
前缀和:prefix = [0, 1, 3, 6](prefix[i]表示前i堆的和,prefix[0]=0)。

定义 dp[i][j] 为合并第 i 堆到第 j 堆(闭区间)成一堆的最小成本。
dp[1][3] 是我们要求的。

  1. 分割点 k=1:先合并 [1, 2] 再与 3 合并。

    • 合并 [1,2]:成本 dp[1][2] = (1+2)^2 = 9。合并后这堆的数量是 sum(1,2) = 3
    • 3 (新堆) 与 3(原第三堆)合并:成本 (3+3)^2 = 36
    • 总成本dp[1][2] + dp[3][3] + (sum(1,2) + stones[3])^2?不对,因为dp[i][j]已经包含了合并ij的所有成本,所以不能直接加(3+3)^2
    • 正确的递推是:dp[1][3]k=1 时 = dp[1][1] + dp[2][3] + (sum(1,3))^2
      • dp[1][1] = 0(一堆无需合并)。
      • dp[2][3] = (2+3)^2 = 25
      • 总区间和 sum(1,3) = 6,平方为36。
      • 所以成本 = 0 + 25 + 36 = 61
  2. 分割点 k=2:先合并 [2,3] 再与 1 合并。

    • dp[1][3] = dp[1][2] + dp[3][3] + (sum(1,3))^2
      • dp[1][2] = 9
      • dp[3][3] = 0
      • sum(1,3)=6,平方36。
      • 所以成本 = 9 + 0 + 36 = 45

比较 6145,最小值为 45
所以对于示例 [1, 2, 3],最小总成本是 45,而不是29。29 这个数字可能是其他题目的答案(比如和等于乘积)。让我们验证题目:成本是和的平方。那么 [1,2,3] 的结果就是 45。如果我的问题描述是“求最小总得分”,那么示例输出应该对应地改为 45。为了讲解的严谨性,我们[1, 2, 3] 的最小总成本 45 为正确答案来继续讲解。

解题思路分析

这是一道典型的区间动态规划问题。

  1. 状态定义:我们关心的是一个连续区间 [i, j] 内的石子合并问题。定义 dp[i][j] 表示将下标从 ij 的这 (j-i+1) 堆石子合并成一堆的最小总成本。最终答案是 dp[0][n-1]
  2. 状态转移:如何得到 dp[i][j]?考虑最后一次合并操作。在将区间 [i, j] 最终合并成一堆之前,它一定是由两堆合并而来的。假设最后合并时,左边那大堆是由区间 [i, k] 合并成的,右边那大堆是由区间 [k+1, j] 合并成的(其中 i <= k < j)。
    • 合并 [i, k] 的最小成本是 dp[i][k]
    • 合并 [k+1, j] 的最小成本是 dp[k+1][j]
    • 最后一步合并的成本是:(sum(i, j))^2,其中 sum(i, j) 是区间 [i, j] 所有石子的总数(即最终合并的两大堆各自石子数之和)。
    • 因此,对于这个特定的分割点 k,总成本为:dp[i][k] + dp[k+1][j] + (sum(i, j))^2
    • 为了得到 dp[i][j] 的最小值,我们需要枚举所有可能的分割点 k,并取上述计算结果的最小值。
  3. 基础情况:当区间只有一堆石子时(即 i == j),不需要合并,成本为 0。所以 dp[i][i] = 0
  4. 计算顺序:由于计算大区间 [i, j] 时,依赖于其内部更小的区间 [i, k][k+1, j],所以我们必须按照区间长度从小到大的顺序来计算。先计算所有长度为2的区间,然后是长度为3的区间,...,最后是长度为n的区间。
  5. 区间和快速计算:为了高效计算任意区间 [i, j] 的和,我们可以预先计算一个前缀和数组 prefix,其中 prefix[0]=0prefix[i]=stones[0]+stones[1]+...+stones[i-1]。这样 sum(i, j) = prefix[j+1] - prefix[i]

详细解题步骤

我们以 stones = [1, 2, 3] 为例,n = 3

  1. 初始化

    • 创建 dp 表,大小为 n x n,所有元素初始化为一个很大的数(比如无穷大 inf)。
    • 设置基础情况:dp[i][i] = 0 对于所有 i。因为一堆石子不需要合并。
    • 计算前缀和 prefix:
      • prefix[0] = 0
      • prefix[1] = stones[0] = 1
      • prefix[2] = prefix[1] + stones[1] = 1 + 2 = 3
      • prefix[3] = prefix[2] + stones[2] = 3 + 3 = 6
  2. 按区间长度 len 从小到大计算

    • 长度 len = 2(即区间包含2堆石子):
      • 区间 [0, 1] (石子 [1, 2]):
        • 只有一种合并方式,k 只能等于 0
        • sum = prefix[2] - prefix[0] = 3 - 0 = 3
        • dp[0][1] = dp[0][0] + dp[1][1] + sum^2 = 0 + 0 + 3^2 = 9
      • 区间 [1, 2] (石子 [2, 3]):
        • sum = prefix[3] - prefix[1] = 6 - 1 = 5
        • dp[1][2] = dp[1][1] + dp[2][2] + 5^2 = 0 + 0 + 25 = 25
    • 长度 len = 3(即区间包含3堆石子,我们的目标区间):
      • 区间 [0, 2] (石子 [1, 2, 3]):
        • 枚举分割点 k01
        • sum = prefix[3] - prefix[0] = 6 - 0 = 6
        • k = 0
          • dp[0][2] 候选值 = dp[0][0] + dp[1][2] + 6^2 = 0 + 25 + 36 = 61
        • k = 1
          • dp[0][2] 候选值 = dp[0][1] + dp[2][2] + 6^2 = 9 + 0 + 36 = 45
        • 取最小值:dp[0][2] = min(61, 45) = 45
  3. 得到答案dp[0][n-1]dp[0][2] = 45

复杂度分析

  • 时间复杂度:O(n³)。我们需要枚举所有区间 O(n²),对于每个区间,需要枚举其内部所有可能的分割点 O(n)
  • 空间复杂度:O(n²),用于存储 dp 表。使用前缀和数组的空间为 O(n)。

核心要点与总结

这道题是区间DP的“教科书式”应用,清晰地展示了该类问题的思考框架:

  1. 定义以区间为状态dp[i][j] 表示解决子问题 [i, j] 的最优解。
  2. 寻找合并/分割点:考虑区间最后一步是如何形成的(通常是通过一个分割点 k 将区间分成两个子区间分别处理,再合并)。
  3. 状态转移方程dp[i][j] = min_{i<=k<j} { dp[i][k] + dp[k+1][j] + cost(i, j, k) }。其中 cost 函数是合并两个子区间结果时的代价,这里非常关键:在这个问题中,cost(sum(i, j))^2请注意,这个代价与分割点 k 无关,只与整个区间的总和有关。这是因为无论你怎么分割、按什么顺序合并,最终把整个区间 [i, j] 合成一堆时,你都需要将它的所有石子加总一次,而最后一步合并的成本正是这个总和的平方。这简化了转移方程。
  4. 确定计算顺序:从小区间到大区间,保证在计算大区间时,它所依赖的小区间已经计算完毕。
  5. 利用前缀和优化:快速计算区间和,使 cost 函数的计算为 O(1)。

通过理解这个经典模型,你可以触类旁通,解决很多其他“合并相邻元素”的区间DP问题,区别往往在于 cost 函数的定义(例如,有的题目成本是两堆石子数的乘积,或者是其他函数)。

好的,我们来看一个在“区间动态规划”领域中具有代表性,且在您提供的列表中 未被详细、明确地作为独立标题讲述过 的经典问题: 题目:合并相邻石子堆的最小成本问题(每次合并的得分为两堆石子数量之和的平方,求最小总得分) 问题描述 我们有一排 n 堆石子,排成一行。第 i 堆石子有 stones[i] 个石子。我们的目标是 将所有石子堆合并成一堆 。 合并的规则是:每次只能合并 相邻 的两堆石子。合并的 成本 (或称为得分)定义为被合并的两堆石子的 石子数量之和的平方 。 例如,合并两堆石子,数量分别为 a 和 b ,则本次合并的成本为 (a + b)^2 。 在整个过程中,你需要不断地合并相邻堆,直到只剩下一堆为止。不同的合并顺序会导致 总成本 不同。 请你设计一个算法,计算出将所有石子堆合并成一堆的 最小总成本 。 示例 : 输入: stones = [1, 2, 3] 输出: 29 解释: 方案一:先合并前两堆,成本 (1+2)^2 = 9 ,得到新堆 [3, 3] ;再合并这两堆,成本 (3+3)^2 = 36 ;总成本 9 + 36 = 45 。 方案二:先合并后两堆,成本 (2+3)^2 = 25 ,得到新堆 [1, 5] ;再合并这两堆,成本 (1+5)^2 = 36 ;总成本 25 + 36 = 61 。 方案三(最优):先合并第1和第3堆?不可以,因为它们不相邻。 先合并1和2得 [3, 3] ,成本9;再合并得36,总45。 先合并2和3得 [1, 5] ,成本25;再合并得36,总61。 等等...等一下,似乎没有比45更小的了?让我再算算。 等等,我上面的计算有误。 关键在于合并后新堆的石子数是两堆之和,这个和会参与下一次合并 。 让我们用区间DP来正确计算: 对于 [1, 2, 3] : 前缀和: prefix = [0, 1, 3, 6] (prefix[ i]表示前i堆的和,prefix[ 0 ]=0)。 定义 dp[i][j] 为合并第 i 堆到第 j 堆(闭区间)成一堆的最小成本。 dp[1][3] 是我们要求的。 分割点 k=1 :先合并 [1, 2] 再与 3 合并。 合并 [1,2] :成本 dp[1][2] = (1+2)^2 = 9 。合并后这堆的数量是 sum(1,2) = 3 。 将 3 (新堆) 与 3 (原第三堆)合并:成本 (3+3)^2 = 36 。 总成本 : dp[1][2] + dp[3][3] + (sum(1,2) + stones[3])^2 ?不对,因为 dp[i][j] 已经包含了合并 i 到 j 的所有成本,所以不能直接加 (3+3)^2 。 正确的递推是: dp[1][3] 当 k=1 时 = dp[1][1] + dp[2][3] + (sum(1,3))^2 。 dp[1][1] = 0 (一堆无需合并)。 dp[2][3] = (2+3)^2 = 25 。 总区间和 sum(1,3) = 6 ,平方为36。 所以成本 = 0 + 25 + 36 = 61 。 分割点 k=2 :先合并 [2,3] 再与 1 合并。 dp[1][3] = dp[1][2] + dp[3][3] + (sum(1,3))^2 。 dp[1][2] = 9 。 dp[3][3] = 0 。 sum(1,3)=6 ,平方36。 所以成本 = 9 + 0 + 36 = 45 。 比较 61 和 45 ,最小值为 45 。 所以对于示例 [1, 2, 3] ,最小总成本是 45 ,而不是29。 29 这个数字可能是其他题目的答案(比如和等于乘积)。让我们验证题目:成本是和的平方。那么 [1,2,3] 的结果就是 45 。如果我的问题描述是“求最小总得分”,那么示例输出应该对应地改为 45 。为了讲解的严谨性,我们 以 [1, 2, 3] 的最小总成本 45 为正确答案 来继续讲解。 解题思路分析 这是一道典型的 区间动态规划 问题。 状态定义 :我们关心的是一个连续区间 [i, j] 内的石子合并问题。定义 dp[i][j] 表示将下标从 i 到 j 的这 (j-i+1) 堆石子 合并成一堆 的最小总成本。最终答案是 dp[0][n-1] 。 状态转移 :如何得到 dp[i][j] ?考虑最后一次合并操作。在将区间 [i, j] 最终合并成一堆之前,它一定是由 两堆 合并而来的。假设最后合并时,左边那大堆是由区间 [i, k] 合并成的,右边那大堆是由区间 [k+1, j] 合并成的(其中 i <= k < j )。 合并 [i, k] 的最小成本是 dp[i][k] 。 合并 [k+1, j] 的最小成本是 dp[k+1][j] 。 最后一步合并的成本是: (sum(i, j))^2 ,其中 sum(i, j) 是区间 [i, j] 所有石子的总数(即最终合并的两大堆各自石子数之和)。 因此,对于这个特定的分割点 k ,总成本为: dp[i][k] + dp[k+1][j] + (sum(i, j))^2 。 为了得到 dp[i][j] 的最小值,我们需要 枚举所有可能的分割点 k ,并取上述计算结果的最小值。 基础情况 :当区间只有一堆石子时(即 i == j ),不需要合并,成本为 0 。所以 dp[i][i] = 0 。 计算顺序 :由于计算大区间 [i, j] 时,依赖于其内部更小的区间 [i, k] 和 [k+1, j] ,所以我们必须按照 区间长度从小到大 的顺序来计算。先计算所有长度为2的区间,然后是长度为3的区间,...,最后是长度为n的区间。 区间和快速计算 :为了高效计算任意区间 [i, j] 的和,我们可以预先计算一个 前缀和数组 prefix ,其中 prefix[0]=0 , prefix[i]=stones[0]+stones[1]+...+stones[i-1] 。这样 sum(i, j) = prefix[j+1] - prefix[i] 。 详细解题步骤 我们以 stones = [1, 2, 3] 为例, n = 3 。 初始化 : 创建 dp 表,大小为 n x n ,所有元素初始化为一个很大的数(比如无穷大 inf )。 设置基础情况: dp[i][i] = 0 对于所有 i 。因为一堆石子不需要合并。 计算前缀和 prefix : prefix[0] = 0 prefix[1] = stones[0] = 1 prefix[2] = prefix[1] + stones[1] = 1 + 2 = 3 prefix[3] = prefix[2] + stones[2] = 3 + 3 = 6 按区间长度 len 从小到大计算 : 长度 len = 2 (即区间包含2堆石子): 区间 [0, 1] (石子 [1, 2] ): 只有一种合并方式, k 只能等于 0 。 sum = prefix[2] - prefix[0] = 3 - 0 = 3 。 dp[0][1] = dp[0][0] + dp[1][1] + sum^2 = 0 + 0 + 3^2 = 9 。 区间 [1, 2] (石子 [2, 3] ): sum = prefix[3] - prefix[1] = 6 - 1 = 5 。 dp[1][2] = dp[1][1] + dp[2][2] + 5^2 = 0 + 0 + 25 = 25 。 长度 len = 3 (即区间包含3堆石子,我们的目标区间): 区间 [0, 2] (石子 [1, 2, 3] ): 枚举分割点 k 从 0 到 1 。 sum = prefix[3] - prefix[0] = 6 - 0 = 6 。 当 k = 0 : dp[0][2] 候选值 = dp[0][0] + dp[1][2] + 6^2 = 0 + 25 + 36 = 61 。 当 k = 1 : dp[0][2] 候选值 = dp[0][1] + dp[2][2] + 6^2 = 9 + 0 + 36 = 45 。 取最小值: dp[0][2] = min(61, 45) = 45 。 得到答案 : dp[0][n-1] 即 dp[0][2] = 45 。 复杂度分析 时间复杂度 :O(n³)。我们需要枚举所有区间 O(n²) ,对于每个区间,需要枚举其内部所有可能的分割点 O(n) 。 空间复杂度 :O(n²),用于存储 dp 表。使用前缀和数组的空间为 O(n)。 核心要点与总结 这道题是区间DP的“教科书式”应用,清晰地展示了该类问题的思考框架: 定义以区间为状态 : dp[i][j] 表示解决子问题 [i, j] 的最优解。 寻找合并/分割点 :考虑区间最后一步是如何形成的(通常是通过一个分割点 k 将区间分成两个子区间分别处理,再合并)。 状态转移方程 : dp[i][j] = min_{i<=k<j} { dp[i][k] + dp[k+1][j] + cost(i, j, k) } 。其中 cost 函数是合并两个子区间结果时的代价, 这里非常关键 :在这个问题中, cost 是 (sum(i, j))^2 。 请注意,这个代价与分割点 k 无关,只与整个区间的总和有关 。这是因为无论你怎么分割、按什么顺序合并,最终把整个区间 [i, j] 合成一堆时,你都需要将它的所有石子加总一次,而最后一步合并的成本正是这个总和的平方。这简化了转移方程。 确定计算顺序 :从小区间到大区间,保证在计算大区间时,它所依赖的小区间已经计算完毕。 利用前缀和优化 :快速计算区间和,使 cost 函数的计算为 O(1)。 通过理解这个经典模型,你可以触类旁通,解决很多其他“合并相邻元素”的区间DP问题,区别往往在于 cost 函数的定义 (例如,有的题目成本是两堆石子数的乘积,或者是其他函数)。