LeetCode312 戳气球


题目

有 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

方法

一般取最值需要遍历所有情况比较后才能得出最值,遍历一般分为自上而下的遍历(dfs)和自下而上的遍历(动态规划),我们可以将戳气球的过程反向思考,变为一排灯先亮哪个,即最后一个戳破的气球为第一个亮起的灯,我们把边界条件添加进数组中,因此此题目可看作求(0,n+1)范围内的亮灯问题,当选取下标为k的灯为(i,j)范围第一个亮的灯的话,他的硬币数为val(i)val(k)val(j),下一步考虑(i,k)区间和(k,j)区间的,因此我们可得出状态方程:
dp[i,j] = max(val(i)val(k)val(j)) + dp[i,k]+dp[k,j]

深度优先遍历

class Solution {
    public int maxCoins(int[] nums) {
        int length = nums.length;
        int[] val = new int[length+2];
        val[0] = val[length+1] = 1;
        for(int i=0;i=j-1){
            return 0;
        }
        int res = 0;
        for(int k = i+1;k

深度优先遍历+记忆化优化

深度优先遍历计算过程中一些区间重复计算,因此可以用数组保存区间最大值

  • 时间复杂度:O(n^3),n为数组长度
  • 空间复杂度:O(n^2)

(区间)动态规划法

第一步:把边界条件加入数组,重新构造一个带边界的数组val
第二步:按动态规划状态方程计算,由于dp[i,j]需要dp[i,k],dp[k,j],因此i需要从大到小,j则从i+2到最后,所以i从n-1到0

  • 时间复杂度:O(n^3),n为数组长度
  • 空间复杂度:O(n^2)
class Solution {
    public int maxCoins(int[] nums) {
        int length = nums.length;
        int[] val = new int[length+2];
        int[][] dp = new int[length+2][length+2];
        val[0] =  val[length+1] = 1;
        for(int i=0;i=0;i--){
            for(int j=i+2;j<=length+1;j++){
                for(int k=i+1;k