Leetcode 312 打气球 Burst Balloons C++ 史上最详细题解系列

 

每天更新一道python or C++ leetcode题,力求讲解清晰准确,客官们可以点赞或者关注。

 

题目:

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

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

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

说明:

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

示例:

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

 

先解释一下为什么这一次直接跳到312,因为这题实在是。。。太巧妙了,做的时候有感而发。

首先要知道的是要采用dp的方法做。

难点1

dp各项表示什么?

这关乎到做题选手的建模能力,我们代码中选择的是dp[i][j]等于从第i个数到第j个数的这个区间内的乘积最大值(包括i,j)

 

难点2

如何找递归式?

这里就更巧妙了,总的思路是先确定长度较小的区间,然后在这个区间里选一个数k成为最后一个被打爆的气球,dp[left][i-1]就是左边部分被打爆的最大值,dp[i+1][right]就是右边部分被打爆的最大值,所以我们只需要算最后一次打爆气球的分数再加上2旁区间打爆所有气球的最大值即可。读上面这句话三遍或以上直至读懂80,读代码:

// Non-recursion
class Solution {
public:
    int maxCoins(vector& nums) {
        int n = nums.size();//记录数组大小为n
        nums.insert(nums.begin(), 1);//在数组前加1
        nums.push_back(1);//后加1
        vector > dp(nums.size(), vector(nums.size() , 0));//构造dp表,注意这时候nums.size()是加过2了的
        for (int len = 1; len <= n; ++len) {//遍历长度,这个长度len指的是区间[left,right]的长度
            for (int left = 1; left <= n - len + 1; ++left) {//遍历这个区间的起始点left
                int right = left + len - 1;//通过上面的长度遍历求区间的结束点right
                for (int k = left; k <= right; ++k) {//遍历这个区间中的每个点
                    dp[left][right] = max(dp[left][right], nums[left - 1] * nums[k] * nums[right + 1] + dp[left][k - 1] + dp[k + 1][right]);//dp[left][right]表示从left->right中所有点的连乘的最大值,包括left,right。
                    //这个递推式的解释是,我选择k作为最后一个被消除的元素,那么这个区间的连乘的最大值为
                    //nums[left-1]*nums[k]*nums[right+1](这时候left,right都不在了,因为k才是这个区间的最后一个元素,所以这个区间2边相邻的元素是nums[left-1],nums[right+1].这个式子表示最后一次计算的结果,
                    //dp[left][k-1]指的是从left->k-1乘完后的最大值(包括left,k-1),dp[k+1][right]同理
                }
            }
        }
        return dp[1][n];//返回整个最大区间的连乘最大值(第1个到第n个)
    }
};

代码每句都有注释,以各位的水平一定是读得懂的。

读完后有些读者就吐槽了:

1.dp[left][k-1] , dp[k+1][right]是你想求就求啊,你以为这是递归函数?

答:这是这道题最巧妙的地方了,仔细看我们的for循环的顺序,第一次我们取长度为1的区间(也就是每个区间只有一个数),第二次for遍历时候取的长度为2的区间,这时候长度为1的区间的结果已经求出来了!刚好可以被长度为2的区间用到!伟大的dp!

2.行行行,那有些时候dp[k+1][right]里的k+1 > right了怎么说?

答:确实会出现这种情况,比如遍历长度为1的区间时。但是这并不影响做题,因为那些都会是0,经过初始化后还没动。

 

总结:

dp真的是最难的题!

 

你可能感兴趣的:(C++,算法Leetcode,数据结构,数据结构与算法,Leetcode,刷题记录python,C++,JAVA)