312. 戳气球 - 力扣(Leetcode)
有 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
思路
对于给定的一排气球,我们可以在左右两侧添加1。左右添加1后,设长度是n。那么我们此时求解的范围是(0, n)区间能够获得最大金币数量。注意,是全开区间。此时,求解的问题可设为f(0,n)。
此时,对于(0, n)区间内,无论怎么戳破气球,肯定会有一个是最后一个戳破的。假如,最后戳破的是2号。那么在戳破2号之前,我们就已经将(0,2)和(2,5)范围内的气球戳破了。还得提醒一下,是全开区间。即,在戳破2号之前,我们已经获得了
f(0, 2)和f(2, 5)。
所以,戳破2号气球时,获得的值是nums[0]*nums[2]*nums[5]。在(0, n)区间内获得金币值就是f(0, n)=f(0, 2)+f(2, 5) + nums[0]*nums[2]*nums[5]
更通俗一点说,对于(i, j)区间内,无论怎么戳破气球,肯定会有一个最后一个戳破的。假如,最后戳破的是k号。那么在戳破k号之前,我们就已经将(i, k)和(k, j)范围内的气球戳破了。还得提醒一下,是全开区间。即,在戳破k号之前,我们已经获得了f(i, k)和f(k, j)。
所以,戳破k号气球时,获得的值是nums[i]*nums[k]*nums[j]。在(i, j)区间内获得金币值就是f(i,j) = f(i, k)+f(k, j) + nums[i]*nums[k]*nums[j]。
上述内容我们假定是某一位置做最后一个戳破的位置,求出来该位置获得金币值。
最终的求解方案就是,对于(i, j)全开区间,每一个位置都尝试是最后一个戳破的位置,答案必在其中,取最大值即可。
暴力递归
func maxCoins(nums []int) int {
nums = append([]int{1}, nums...)
nums = append(nums, 1)
n := len(nums)
return f(nums, 0, n-1)
}
func f(nums []int, i, j int) int {
// 因为区间范围是(i, j)全开区间,所以i+1==j时,
// 区间内是没有数的,获取的金币数量为0
if i+1 == j {
return 0
}
res := 0
for k := i+1; k<j; k++{
// 以k位置作为最后戳破的位置,求出来该位置获得金币数量
v := f(nums, i, k) + f(nums, k, j)+ nums[i]*nums[k]*nums[j]
// 取每一个位置可能的最大值
res = max(res, v)
}
return res
}
func max(a, b int)int{
if a > b {
return a
}
return b
}
暴力递归转动态规划
暴力递归中有两个变量,所以我们用一个二维dp。dp的size根据暴力递归中的i,j范围设置。 于是我们现在有了一张二维dp表。
res := 0
for k := i+1; k<j; k++{
// 以k位置作为最后戳破的位置,求出来该位置获得金币数量
v := f(nums, i, k) + f(nums, k, j)+ nums[i]*nums[k]*nums[j]
// 取每一个位置可能的最大值
res = max(res, v)
}
return res
根据暴力递归中如上代码可以知道,f(i,j)值的得出依赖于f(i, k)和f(k, j)。我们在dp表中表示出dp[i][j]依赖的位置,可以清楚地看出,dp[i][j]依赖于左边和下边。于是,dp表可以从下到上,从左到右填写。
func maxCoins(nums []int) int {
nums = append([]int{1}, nums...)
nums = append(nums, 1)
n := len(nums)
dp := make([][]int, n)
for i:=0; i<n; i++{
dp[i] = make([]int, n)
}
for i:=n-1; i>=0; i-- {
for j:=i; j<n; j++{
// 接下来6行是暴力递归主体,就是把从f(i, j)拿值变成了从dp[i][j]拿值
res := 0
for k := i+1; k<j; k++{
v := dp[i][k] + nums[i]*nums[k]*nums[j] + dp[k][j]
res = max(res, v)
}
dp[i][j] = res
}
}
return dp[0][n-1]
}
func getValue(dp [][]int,i, j int) int {
if i+1 == j {
return 0;
}
return dp[i][j];
}
func max(a, b int)int{
if a > b {
return a
}
return b
}