每日算法系列【LeetCode 312】戳气球

简介: 每日算法系列【LeetCode 312】戳气球

题目描述

有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。

求所能获得硬币的最大数量。

示例1

输入:
[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

提示

  • 你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。
  • 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

题解

dfs+记忆化搜索

对于区间 [l, r] ,我们考虑最后一个被戳破的气球 k ,那么之前的步骤我们可以分为两步,也就是求 [l, k-1] 和 [k+1, r] 之间的最大分数。

那么为什么不考虑先戳破 k 呢?因为这样的话 [l, k-1] 和 [k+1, r] 就会连接在一起,两个子状态就不能独立计算了,互相会产生影响。

两个子区间的最大的分算完之后,最后 k 的得分就是 nums[l-1] * nums[k] * nums[r+1] ,取使得总得分最高的 k 就行了。

有一个小技巧就是,提示里也说了,就是刚开始的时候在首尾各添加一个分数为 1 的虚拟气球。

但是直接这样递归会超时,因为有很多的子状态都重复计算了,所以可以用一个全局的数组保存每个状态的分数。初始化为 -1 ,如果某个状态计算过了,就直接返回它的值就行了,不然就递归计算。

动态规划

上面的方法是自顶向下的,其实也可以转化成自底向上的,也就是从小的区间开始算起,最后算最大的,这就是动态规划的方法,具体的实现细节和上面是一模一样的。

代码

dfs+记忆化搜索(c++)

class Solution {
public:
    static const int N = 510;
    int dp[N][N];
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        memset(dp, -1, sizeof dp);
        int res = dfs(1, n, nums);
        return res;
    }
    int dfs(int l, int r, vector<int>& nums) {
        if (dp[l][r] != -1) return dp[l][r];
        if (l > r) return 0;
        int res = 0;
        for (int k = l; k <= r; ++k) {
            res = max(res, nums[l-1]*nums[k]*nums[r+1]+dfs(l, k-1, nums)+dfs(k+1, r, nums));
        }
        return dp[l][r] = res;
    }
};

动态规划(c++)

class Solution {
public:
    static const int N = 510;
    int dp[N][N];
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        memset(dp, 0, sizeof dp);
        for (int len = 1; len <= n; ++len) {
            for (int i = 1; i+len-1 <= n; ++i) {
                int j = i + len - 1;
                for (int k = i; k <= j; ++k) {
                    dp[i][j] = max(dp[i][j], nums[i-1]*nums[k]*nums[j+1] + dp[i][k-1] + dp[k+1][j]);
                }
            }
        }
        return dp[1][n];
    }
};

dfs+记忆化搜索(python)

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        n = len(nums)
        nums = [1] + nums + [1]
        self.dp = [[-1]*(n+2) for _ in range(n+2)]
        res = self.dfs(1, n, nums)
        return res
    def dfs(self, l, r, nums):
        if self.dp[l][r] != -1:
            return self.dp[l][r]
        if l > r:
            return 0
        res = 0
        for k in range(l, r+1):
            res = max(res, nums[l-1]*nums[k]*nums[r+1]+self.dfs(l, k-1, nums)+self.dfs(k+1, r, nums))
        self.dp[l][r] = res
        return res

动态规划(python)

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        n = len(nums)
        nums = [1] + nums + [1]
        dp = [[0]*(n+2) for _ in range(n+2)]
        for l in range(1, n+1):
            for i in range(1, n-l+2):
                j = i + l - 1
                for k in range(i, j+1):
                    dp[i][j] = max(dp[i][j], nums[i-1]*nums[k]*nums[j+1] + dp[i][k-1] + dp[k+1][j])
        return dp[1][n]
相关文章
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
15天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
1月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
27 2
|
3月前
|
算法 Java
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
52 6
|
3月前
|
人工智能 算法 Java
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
50 1
|
3月前
|
存储 算法 Java
LeetCode经典算法题:预测赢家+香槟塔java解法
LeetCode经典算法题:预测赢家+香槟塔java解法
61 1
|
3月前
|
算法 vr&ar 虚拟化
【Leetcode刷题Python】452. 用最少数量的箭引爆气球
首先对气球的结束坐标进行排序,然后使用贪心算法,按顺序选择每个气球的结束坐标作为射箭的点,只要气球的开始坐标大于前一个气球的结束坐标,就意味着需要多一支箭,更新最小箭数。这种方法可以确保以最少的箭数引爆所有气球。
19 1
|
3月前
|
存储 算法 Java
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
79 0
|
3月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
43 0
|
3月前
|
算法 Java
LeetCode初级算法题:环形链表+排列硬币+合并两个有序数组java解法
LeetCode初级算法题:环形链表+排列硬币+合并两个有序数组java解法
55 0
下一篇
无影云桌面