石子合并问题(每次合并的得分为两堆石子数量之和的立方,求最小总得分)
题目描述
给定一排 n 堆石子,每堆石子的数量用一个数组 stones[0..n-1] 表示。游戏规则如下:
- 每次只能合并相邻的两堆石子,合并成一堆新石子,其数量为原来两堆石子数量之和。
- 每次合并的得分定义为两堆石子数量之和的立方。
- 游戏一直进行,直到只剩下一堆石子为止。
目标是求出最小总得分(即所有合并操作的得分之和的最小值)。
例如,假设 stones = [1, 2, 3]:
- 方案1:先合并前两堆
(1+2)=3,得分(1+2)^3 = 27,剩下[3, 3];再合并两堆(3+3)=6,得分(3+3)^3 = 216,总得分27 + 216 = 243。 - 方案2:先合并后两堆
(2+3)=5,得分(2+3)^3 = 125,剩下[1, 5];再合并两堆(1+5)=6,得分(1+5)^3 = 216,总得分125 + 216 = 341。
所以最小总得分为243。
注意:因为得分函数是立方(而非通常题目中的两数之和),合并顺序对总得分影响更大,需要仔细设计动态规划状态和转移。
问题分析
这个问题是经典的区间动态规划。
我们需要考虑所有可能的合并顺序,并找出总得分最小的方案。由于每次合并相邻两堆,最终状态一定是由某个区间合并成单一堆。因此可以定义如下状态:
-
状态定义
dp[i][j]表示将第i堆到第j堆(闭区间)的石子合并成一堆的最小总得分。
(注意:这里i和j从 0 开始编号。) -
状态转移方程
考虑最后一次合并:将区间[i, j]分成两个子区间[i, k]和[k+1, j],分别合并成两堆,然后再将这两堆合并。
设子区间[i, k]的石子总数为sum(i, k),子区间[k+1, j]的石子总数为sum(k+1, j),合并这两堆的得分为(sum(i, k) + sum(k+1, j))^3。
但是注意:sum(i, k) + sum(k+1, j) = sum(i, j),即整个区间的石子总数。
所以合并这两堆的得分是(sum(i, j))^3,与k无关!
因此,转移方程为:
\[ dp[i][j] = \min_{i \le k < j} \left\{ dp[i][k] + dp[k+1][j] + (sum(i, j))^3 \right\} \]
其中 sum(i, j) 是区间 [i, j] 的石子总数。
-
初始条件
当区间长度为 1 时,即i == j,不需要合并,所以dp[i][i] = 0。 -
计算顺序
按区间长度从小到大计算。设长度len从 2 到n,对每个长度枚举起点i,计算j = i + len - 1,然后枚举分割点k。 -
最终答案
答案就是dp[0][n-1]。
示例详解
以 stones = [1, 2, 3] 为例。
步骤 1:计算前缀和
prefix[0] = 0,prefix[1] = 1,prefix[2] = 3,prefix[3] = 6。
这样 sum(i, j) = prefix[j+1] - prefix[i]。
步骤 2:初始化
dp[0][0] = 0, dp[1][1] = 0, dp[2][2] = 0。
步骤 3:计算区间长度 2
[0,1]:sum = 1+2 = 3,得分3^3 = 27,dp[0][1] = dp[0][0] + dp[1][1] + 27 = 27。[1,2]:sum = 2+3 = 5,得分5^3 = 125,dp[1][2] = 125。
步骤 4:计算区间长度 3([0,2])
sum = 1+2+3 = 6,得分 6^3 = 216。
枚举 k:
k=0:dp[0][0] + dp[1][2] + 216 = 0 + 125 + 216 = 341k=1:dp[0][1] + dp[2][2] + 216 = 27 + 0 + 216 = 243
取最小:dp[0][2] = 243。
最终答案:dp[0][2] = 243,与前面手动计算一致。
为什么立方得分使得问题更复杂?
如果是平方得分(即 (sum(i,j))^2),那么合并得分只依赖于当前区间的和,依然与 k 无关,所以转移方程与经典石子合并类似。
立方得分也是一样:合并最后两堆时的得分是 (sum(i,j))^3,也与 k 无关。
因此,在这个问题中,状态转移方程的形式与经典石子合并完全相同,只是把加和改成立方。
但注意:虽然合并最后两堆的得分与 k 无关,但 dp[i][k] 和 dp[k+1][j] 本身包含了子区间合并的得分,这些得分是立方的,所以子问题的最优解会影响最终结果。
因此,我们不能简单地认为“合并顺序不影响总得分”——实际上不同的合并顺序会导致不同的子区间和,进而影响子区间合并的立方得分,所以必须用动态规划求解。
时间复杂度与优化
直接按上述方法计算,时间复杂度为 O(n³),因为有三重循环:
- 第一重:区间长度
len - 第二重:起点
i - 第三重:分割点
k
对于 n 最大几百的情况,O(n³) 是可以接受的。
如果 n 更大(如上千),则需要考虑四边形不等式优化,但本题因为得分是立方,可能不满足单调性,优化需谨慎验证。
总结
这道题是区间动态规划的经典应用,虽然得分函数是立方,但状态转移方程与经典石子合并问题相同,只需将合并得分改为 (区间和)^3。
通过从小到大计算区间最优解,最终得到全局最优解。