当前位置: 面试刷题>> 爆破气球的最小箭头数 (经典算法题500道)


题目描述

你正在玩一个爆破气球的游戏。游戏中有n个气球,编号为从1n,排列在一行上。每个气球上都有一个数字,表示该气球的点数。现在,你需要按照以下规则爆破气球:

  1. 每次你可以选择相邻的两个气球(即索引为ii+1的两个气球,其中i是选择的起始气球的索引,并且i+1小于n),并且爆破它们。
  2. 爆破这两个气球后,你可以获得这两个气球点数乘积的分数。
  3. 爆破后,这两个气球之间的所有气球(如果有的话)都会合并成一个新的气球,其点数为这些气球点数之和。
  4. 注意,在爆破过程中,你不能爆破已经爆破过的气球,也不能爆破编号上不相邻的气球。

游戏的目标是最大化你获得的分数。请问,你需要使用多少个箭头(即进行多少次爆破操作)来爆破所有气球,并使得获得的分数最大?同时,请给出获取最大分数的爆破策略。

解答思路

对于这个问题,实际上“需要多少个箭头”是一个简单的问题:因为除了第一个和最后一个气球外,其他所有气球都需要被爆破一次(与相邻的气球合并),所以总共需要的箭头数为n-1个。

然而,题目的核心在于如何找到爆破策略以获得最大分数。这是一个经典的动态规划问题。

动态规划解法

定义一个二维数组dp[i][j],表示从气球i到气球j(包含ij)之间爆破气球能得到的最大分数。注意,这里的ij实际上代表的是气球之间的“空隙”的索引,即实际爆破的气球是从i+1j-1

状态转移方程

dp[i][j] = max(dp[i][k] + dp[k][j] + points[i+1] * points[k] * points[j]),其中 i < k < j

这里,points[i+1] * points[k] * points[j]表示爆破i+1k之间以及kj之间的气球后,得到的分数加成(因为ki+1j相邻)。

示例代码

以下是使用Python编写的示例代码:

def maxCoins(nums):
    n = len(nums)
    # 在数组两端各添加一个1,方便边界处理
    nums = [1] + nums + [1]
    n += 2
    dp = [[0] * n for _ in range(n)]

    # 遍历子问题的规模
    for length in range(3, n):
        # 遍历所有可能的子问题起点
        for i in range(n - length):
            j = i + length - 1
            # 枚举分割点k
            for k in range(i + 1, j):
                dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j])

    return dp[0][n-1]

# 示例
nums = [3, 1, 5, 8]
print(maxCoins(nums))  # 输出最大分数

对于PHP和JavaScript,基本思路相同,只是语法和数组处理上有所差异。码小课网站中有更多关于动态规划和其他算法的内容分享给大家学习。

推荐面试题