一、 题目
1. 题目描述
- 戳气球
难度:困难
有 n
个气球,编号为0
到 n - 1
,每个气球上都标有一个数字,这些数字存在数组 nums
中。
现在要求你戳破所有的气球。戳破第 i
个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1]
枚硬币。 这里的 i - 1
和 i + 1
代表和 i
相邻的两个气球的序号。如果 i - 1
或 i + 1
超出了数组的边界,那么就当它是一个数字为 1
的气球。
求所能获得硬币的最大数量。
示例 1:
输入:nums = [3,1,5,8]
输出:167
解释:
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
示例 2:
输入:nums = [1,5]
输出:10
提示:
n == nums.length
1 <= n <= 300
0 <= nums[i] <= 100
2. 原题链接
链接: 312. 戳气球
二、 解题报告
1. 思路分析
-
如果正向模拟思考,那每次戳破一个数组状态都发生变化,很难思考,不如我们逆向思考(这正好是记忆化搜索的思考方式)。
-
在这之前我们在数组两边增加两个虚拟节点1,方便处理边界情况。
-
遍历区间内每个气球作为最后一个戳破的气球:
- 假设我们处理开区间(l,r)下标为i的气球,那这个i气球的贡献一定是nums[i]*nums[l]*nums[r],因为i是最后一个戳破的,它一定和数组的边界进行计算。
- 那么这时它再加上左、右区间的戳破最大值,就是这个区间总共的值。
- 而左右区间明显是子问题:
- 左区间(l,i);右区间(i,r)
-
具体实现时:
- 如果开区间边界l+1<=r,显然结果是0
- 否则遍历每个i对ret进行更新。
2. 复杂度分析
最坏时间复杂度O(n3)。
- n是节点数。
- 一共n2个区间,全都遍历到了,每个区间遍历了n次。
3. 代码实现
记忆化搜索
。
class Solution:
def maxCoins(self, nums: List[int]) -> int:
nums = [1] + nums + [1]
@cache
def dfs(l,r):
ret = 0
for i in range(l+1,r):
ret = max(ret, nums[l]*nums[r]*nums[i] + dfs(l,i) + dfs(i,r))
return ret
return dfs(0,len(nums)-1)
三、 本题小结
- 动态规划也能做,更快。