312 戳气球(区间dp)

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 <= 500
  • 0 <= nums[i] <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/burst-balloons

2. 思路分析:

这道题目属于经典的区间dp的问题,对于区间dp的问题我们其实是可以尝试枚举所有长度的区间,为了避免数组越界的问题我们在nums数组左右两边各增加一个值为1的数字,然后枚举起点为i的长度为j的区间,然后尝试在当前长度为j的区间中枚举当前戳掉的气球为第k个气球(第一个气球与最后气球是不能够戳掉的),从较短的区间进行递推一直到最长的那个区间即可。其中我们可以声明长度为n + 2的二维列表,f[i][j]的含义其实是很好想出来的,因为表示的是区间所以含义为区间[i, j]的最大得分,其实本质上是由小的区间范围递推到更大的区间范围,所以最终的结果肯定是最优的(可以看成是暴力枚举的优化,其实也是在尝试所有可能的方案,只是这里的dp是有选择性的记录结果)。

3. 代码如下:

from typing import List


class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        n = len(nums)
        # 往左右两边各插入一个值为1的气球这样而且这两只气球是不能够被戳掉的, 是边界情况
        # dp[i][j]表示的区间[i, j]中第i, j只气球是没有被戳掉的
        nums.insert(0, 1)
        nums.append(1)
        # 多声明几个长度防止越界
        dp = [[0] * (n + 10) for i in range(n + 10)]
        # 区间长度为l
        for l in range(3, n + 3):
            # 枚举所有的区间
            i = 0
            # 枚举所有起点为i长度为l的区间
            while i + l - 1 <= n + 1:
                j = i + l - 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[j] * nums[k])
                i += 1
        return dp[0][n + 1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值