戳气球的最大得分问题(状态转移优化版本)
字数 1641 2025-11-22 19:13:27

戳气球的最大得分问题(状态转移优化版本)

题目描述:
给定n个气球,每个气球上标有一个数字,这些数字存储在数组nums中。现在要求你戳破所有的气球。当你戳破第i个气球时,你可以获得nums[left] × nums[i] × nums[right]个硬币,其中left和right代表与第i个气球相邻的气球。戳破第i个气球后,left和right气球就变成相邻的气球。求能获得的最大硬币数量。

注意:

  • 你可以假设nums[-1] = nums[n] = 1,但它们不是真实存在的气球,所以不会被戳破
  • 0 ≤ n ≤ 500
  • 0 ≤ nums[i] ≤ 100

解题思路:

  1. 问题分析
    这是一个经典的区间动态规划问题。关键在于当我们戳破一个气球时,左右两个原本不相邻的气球会变成相邻,这打破了子问题之间的独立性。我们需要找到一种方法将问题分解为相互独立的子问题。

  2. 关键思路转变
    与其考虑"先戳哪个气球",不如考虑"最后一个戳破的气球是哪个"。当我们确定最后一个戳破的气球k时,问题被自然地分成了两个独立的子问题:戳破k左边所有气球和戳破k右边所有气球。

  3. 状态定义
    定义dp[i][j]表示戳破区间(i, j)内所有气球能获得的最大硬币数,注意这里是开区间,i和j是不戳破的边界气球。

  4. 状态转移方程
    对于区间(i, j),我们枚举最后一个戳破的气球k(i < k < j):
    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] × nums[k] × nums[j])

  5. 边界条件

  • 当区间长度小于等于1时,dp[i][j] = 0(没有气球可戳)
  • 我们添加两个虚拟气球在首尾,值都为1
  1. 计算顺序
    由于大区间依赖于小区间,我们需要按照区间长度从小到大计算。

解题步骤:

步骤1:预处理数组
在原始数组首尾各添加一个值为1的气球:
[1] + nums + [1]

步骤2:初始化DP表
创建一个二维数组dp,大小为(n+2)×(n+2),初始化为0

步骤3:按区间长度递推
对于区间长度len从2到n+1(因为添加了两个虚拟气球):
对于左边界i从0到n+1-len:
计算右边界j = i + len
对于分割点k从i+1到j-1:
计算当前分割方式的得分:
score = dp[i][k] + dp[k][j] + nums[i] × nums[k] × nums[j]
更新dp[i][j] = max(dp[i][j], score)

步骤4:返回结果
dp[0][n+1]就是最终的最大得分

示例演示:
假设nums = [3, 1, 5, 8]
预处理后:balloons = [1, 3, 1, 5, 8, 1]

计算过程:

  • 区间长度2:[0,2]、[1,3]、[2,4]、[3,5]等
  • 区间长度3:[0,3]、[1,4]、[2,5]等
  • 区间长度4:[0,4]、[1,5]等
  • 区间长度5:[0,5](最终结果)

对于区间[0,5],枚举最后一个戳破的气球:

  • k=1:dp[0][1] + dp[1][5] + 1×3×1 = 0 + 167 + 3 = 170
  • k=2:dp[0][2] + dp[2][5] + 1×1×1 = 3 + 40 + 1 = 44
  • k=3:dp[0][3] + dp[3][5] + 1×5×1 = 8 + 40 + 5 = 53
  • k=4:dp[0][4] + dp[4][5] + 1×8×1 = 13 + 8 + 8 = 29
    最大值为170

最终结果:dp[0][5] = 170

这个优化版本的关键在于通过改变思考角度(最后戳破的气球)和定义开区间,使得子问题相互独立,从而可以用动态规划高效解决。

戳气球的最大得分问题(状态转移优化版本) 题目描述: 给定n个气球,每个气球上标有一个数字,这些数字存储在数组nums中。现在要求你戳破所有的气球。当你戳破第i个气球时,你可以获得nums[ left] × nums[ i] × nums[ right ]个硬币,其中left和right代表与第i个气球相邻的气球。戳破第i个气球后,left和right气球就变成相邻的气球。求能获得的最大硬币数量。 注意: 你可以假设nums[ -1] = nums[ n ] = 1,但它们不是真实存在的气球,所以不会被戳破 0 ≤ n ≤ 500 0 ≤ nums[ i ] ≤ 100 解题思路: 问题分析 这是一个经典的区间动态规划问题。关键在于当我们戳破一个气球时,左右两个原本不相邻的气球会变成相邻,这打破了子问题之间的独立性。我们需要找到一种方法将问题分解为相互独立的子问题。 关键思路转变 与其考虑"先戳哪个气球",不如考虑"最后一个戳破的气球是哪个"。当我们确定最后一个戳破的气球k时,问题被自然地分成了两个独立的子问题:戳破k左边所有气球和戳破k右边所有气球。 状态定义 定义dp[ i][ j ]表示戳破区间(i, j)内所有气球能获得的最大硬币数,注意这里是开区间,i和j是不戳破的边界气球。 状态转移方程 对于区间(i, j),我们枚举最后一个戳破的气球k(i < k < j): dp[ i][ j] = max(dp[ i][ j], dp[ i][ k] + dp[ k][ j] + nums[ i] × nums[ k] × nums[ j ]) 边界条件 当区间长度小于等于1时,dp[ i][ j ] = 0(没有气球可戳) 我们添加两个虚拟气球在首尾,值都为1 计算顺序 由于大区间依赖于小区间,我们需要按照区间长度从小到大计算。 解题步骤: 步骤1:预处理数组 在原始数组首尾各添加一个值为1的气球: [ 1] + nums + [ 1 ] 步骤2:初始化DP表 创建一个二维数组dp,大小为(n+2)×(n+2),初始化为0 步骤3:按区间长度递推 对于区间长度len从2到n+1(因为添加了两个虚拟气球): 对于左边界i从0到n+1-len: 计算右边界j = i + len 对于分割点k从i+1到j-1: 计算当前分割方式的得分: score = dp[ i][ k] + dp[ k][ j] + nums[ i] × nums[ k] × nums[ j ] 更新dp[ i][ j] = max(dp[ i][ j ], score) 步骤4:返回结果 dp[ 0][ n+1 ]就是最终的最大得分 示例演示: 假设nums = [ 3, 1, 5, 8 ] 预处理后:balloons = [ 1, 3, 1, 5, 8, 1 ] 计算过程: 区间长度2:[ 0,2]、[ 1,3]、[ 2,4]、[ 3,5 ]等 区间长度3:[ 0,3]、[ 1,4]、[ 2,5 ]等 区间长度4:[ 0,4]、[ 1,5 ]等 区间长度5:[ 0,5 ](最终结果) 对于区间[ 0,5 ],枚举最后一个戳破的气球: k=1:dp[ 0][ 1] + dp[ 1][ 5 ] + 1×3×1 = 0 + 167 + 3 = 170 k=2:dp[ 0][ 2] + dp[ 2][ 5 ] + 1×1×1 = 3 + 40 + 1 = 44 k=3:dp[ 0][ 3] + dp[ 3][ 5 ] + 1×5×1 = 8 + 40 + 5 = 53 k=4:dp[ 0][ 4] + dp[ 4][ 5 ] + 1×8×1 = 13 + 8 + 8 = 29 最大值为170 最终结果:dp[ 0][ 5 ] = 170 这个优化版本的关键在于通过改变思考角度(最后戳破的气球)和定义开区间,使得子问题相互独立,从而可以用动态规划高效解决。