HOT100-29-戳气球
题目描述
有 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:
1 | 输入:nums = [3,1,5,8] |
算法思路
这道题目能看出来不是贪心能解决的,因为朴素的贪心思路其实很简单,每次都保证能获得最大的硬币,或者先把比较小的气球戳破,进而保证留下的相邻气球比较大,乘积就会大,但是很容易验证局部最优无法推导全局最优。
其次想到的方法是类似于二分,假定考虑i=0, j=n
,我们最后戳破的气球是k
,那么最后的到的收益就是nums[i]*nums[k]*nums[j]
,这是固定的,但是我们需要求出在(i, k)
和(k, j)
之间上次戳破的气球,记作a1, a2
,使得nums[i]*nums[a1]*nums[k]
和nums[k]*nums[a2]*nums[j]
再加上刚刚的nums[i]*nums[k]*nums[j]
总和最大;很明显,这已经不是一个简单的二分问题,因为你无法直接确定分割点在哪,我们需要枚举每一个可能的k, a1, a2……,因此就想出来这是一道动态规划的题目!
这里借鉴的是评论区题解的一种做法。大致思路是这样的:
把这个题目倒过来看,假设需要在
(i, j)
这个区间戳气球可以转化为不断添加
nums[k], i < k < j
,先添加的气球是最后戳破的则状态转移就是:
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j])
这里为了避免
nums[i-1]
越界,拷贝一个新数组a(n + 2, 1)
,使得a[i] = nums[i-1]
特别注意这里是开区间,枚举中间的
k
;最后返回
dp[0][n+1]
即可
代码如下:
1 | class Solution { |