【LeetCode】戳气球 [H](记忆化搜索)

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

提示:

  • n == nums.length
  • 1 <= n <= 300
  • 0 <= nums[i] <= 100

二、代码

class Solution {
    public int maxCoins(int[] nums) {
        // 我们将数组扩大两个位置,用来表示原本左右两个边界,这样可以在写代码的过程中减少很多的边界条件讨论
        int[] arr = new int[nums.length + 2];
        // 将左边界设置为1
        arr[0] = 1;
        // 将右边界设置为1
        arr[nums.length + 1] = 1;
        // 将原数组中的数转移到新数组
        for (int i = 1; i <= nums.length; i++) {
            arr[i] = nums[i - 1];
        }

        // 加缓存
        int dp[][] = new int[nums.length + 2][nums.length + 2];

        return process(1, nums.length, arr, dp);
    }

    public int process(int l, int r, int[] arr, int[][] dp) {
        // 如果已经计算过这个结果,直接返回
        if (dp[l][r] != 0) {
            return dp[l][r];
        }

        // 如果arr[L..R]范围上只有一个气球,直接打爆即可
        if (l == r) {
            return arr[l - 1] * arr[l] * arr[r + 1];
        }
        
        // 选择一:让l位置最后爆    当l最后爆的时候,它左右两边距离最近的并且没有爆掉的就是l-1和r+1,因为这个函数保证了l-1和r+1一定没有爆。并且在l+1~r这个范围内,能保证左边界l和右边界r+1一定没有爆,这样就可以调preocess函数了。
        int coins1 = arr[l - 1] * arr[l] * arr[r + 1] + process(l + 1, r, arr, dp);
        // 选择二:让r位置最后爆
        int coins2 = arr[l - 1] * arr[r] * arr[r + 1] + process(l, r - 1, arr, dp);
        int max = coins1 > coins2 ? coins1 : coins2;
        // 选择三:尝试让l~r之间的位置最后爆
        for (int i = l + 1; i < r ; i++) {
            // 这个最后爆的位置就将整个区间分成了两份,并且左右两个分出来的区间都能满足左右两个边界没有爆,所以可以调用preocess函数
            int coins3 = arr[l - 1] * arr[i] * arr[r + 1] + process(l, i - 1, arr, dp) +  process(i + 1, r, arr, dp);
            max = coins3 > max ? coins3 : max;
        }

        // 记录下结果缓存
        dp[l][r] = max;
        // 将所有可能的选择方案中选择得分最多的一个返回
        return max;
    }
}

三、解题思路 

关键在于递归函数如何设计,就设定preocess方法是求arr[L...R]打爆所有气球,最大得分是什么,并且此时[L-1] 和 [R+1]位置的气球一定没有爆。

你可能感兴趣的:(#,LeetCode,#,算法,算法,记忆化搜索,动态规划,LeetCode,312,Java)