题目 戳气球
有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 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
示例:
输入: [3,1,5,8]
输出: 167
解释:
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 315 + 358 + 138 + 181 = 167
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/burst-balloons
思路
我自己想了半天动归的思路,但是都没有什么结果。看了看官解才意识到居然是一道时间复杂度O(n3)的题目,确实是一道很困难的题目。想要得到状态转移方程,就需要想到采用分治法降低问题难度。例如想到将戳破气球反过来,改为依次添加气球,其实不算难事,但是要运用分治法的思路来看待添加气球的过程。也就是得到如下的方程:
F(left, right) = nums[left] * nums[middle] * nums[right] + F(left, middle) + F(middle, right)
其中,left是左端点,right是右端点,middle为中间将要添加进去的那个气球。
也就是问题可以通过分治思路得到一个正确的求解方向:添加一个气球进去,求一下左中右三个气球的乘积,再递归去考虑中气球与左边气球构成的左右端点,再加入气球会是什么结果,和中气球与右气球中间再次加入气球会是什么结果。一直递归到左右两个端点气球是相邻的了,那么这个时候没右气球可以插入了,就有:
F(left, right) = 0 (left >= right - 1)
这个时候大体方向的思路是有了,但是很明显时间复杂度不止O(n3),因为还没有采用动态规划的空间换时间,存储会被重复计算到的内容。这个时候就需要分析一下会被重复计算到的值是什么。
拿题目中举的例子,输入为[3,1,5,8]来进行计算:
序号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
值 | 1 | 3 | 1 | 5 | 8 | 1 |
备注 | 左端点 | 右端点 |
这里参考了一下官解的思路,设置了值为1的左端点和右端点,方便再插入第一个元素的时候进行计算。按照我们之前反向回推,”插入“气球的思路来看,最初的序列是这样:
序号 | 0 | 5 | |
---|---|---|---|
值 | 1 | 1 | |
备注 | 左端点 | 右端点 |
现在要处理的问题是从1——4的数字中选一个插入,那么依次来看一下情况:
序号 | 0 | 1 | 5 |
---|---|---|---|
值 | 1 | 3 | 1 |
备注 | 左端点 | 右端点 |