【LeetCode】312.戳气球

115 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

题目

有 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

题解

思路

  • 每次从数组中删除一个气球的操作不好执行,那么我们可以反着来,往数组里面添加气球。
  • 首先要处理边界问题,我们可以把 nums 数组前面和后面都增加一个 1,索引下标为 0 和 n+1,这样久可以不用判断边界。
  • 定义 solve(l,r) 代表区间 [l, r] 能够获得的最大数。那么我们要求的便是 solve(0, n+1)。如何往里面添加气球,我们可以假设我们确定边界 l 和 r,因此我们可以往中间选取一个值进行添加气球,那么可以获得的硬币为 rec[mid] * rec[l] * rec[r],然后还要加上左右区间能获得的硬币数: solve(l, mid) + solve(mid, r),这样便使用分治法实现了从上到下的求解过程。
  • 最后还可以使用动态规划来从下到上进行求解。

代码

class Solution {
public:
    vector<vector<int>> dp; //记忆化
    vector<int> rec; //记录气球值
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        rec.push_back(1);
        for (int i = 0; i < n; i++) {
            rec.push_back(nums[i]);
        }
        rec.push_back(1);
        dp.resize(n + 2, vector<int>(n+2, -1));
        return solve(0, n + 1); 
    }
    int solve(int l, int r) {
        if (l >= r - 1) return 0; 
        if (dp[l][r] != -1) return dp[l][r];
        for (int mid = l + 1; mid < r; mid++) {
            int sum = rec[mid] * rec[l] * rec[r];
            sum += solve(l, mid) + solve(mid, r);
            dp[l][r] = max(sum, dp[l][r]);
        }
        return dp[l][r];
    }
};

结语

业精于勤,荒于嬉;行成于思,毁于随。