您的位置:首页 > 编程语言 > C语言/C++

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

2018-08-21 11:05 531 查看

 

每天更新一道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
     = 1
    ,但注意它们不是真实存在的所以并不能被戳破。
  • 0 ≤ 
    n
     ≤ 500, 0 ≤ 
    nums[i]
     ≤ 100

示例:

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

 

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

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

难点1

dp各项表示什么?

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

 

难点2

如何找递归式?

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

[code]// Non-recursion
class Solution {
public:
int maxCoins(vector<int>& nums) {
int n = nums.size();//记录数组大小为n
nums.insert(nums.begin(), 1);//在数组前加1
nums.push_back(1);//后加1
vector<vector<int> > dp(nums.size(), vector<int>(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]
;//返回整个最大区间的连乘最大值(第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真的是最难的题!

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: