题目:
Given n
balloons, indexed from 0
to n-1
.
Each balloon is painted with a number on it represented by array nums
. You are asked
to burst all the balloons. If the you burst balloon i
you will get nums[left]
* nums[i] * nums[right]
coins. Here left
and right
are
adjacent indices of i
. After the burst, the left
and right
then
becomes adjacent.
Find the maximum coins you can collect by bursting the balloons wisely.
Note:
(1) You may imagine nums[-1] = nums[n] = 1
. They are not real therefore you can not burst
them.
(2) 0 ≤ n
≤ 500, 0 ≤ nums[i]
≤
100
Example:
Given [3, 1, 5, 8]
Return 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
思路:
1、动态规划:动态规划是这道题目最容易想到的解。为了便于处理边界情况,我们给nums的首位分别加入1,然后定义dp[i][j]表示在区间[i,j]内的最优解。显然可以得到如下状态转移方程:dp[i][j] = max(dp[i][k - 1] + dp[k + 1][j] + nums[i - 1] * nums[k] * nums[j + 1] ),其中i <= k <= j。max函数里面的表达式表示最后爆破第k个气球所产生的收益。该算法的时间复杂度是O(n^3),空间复杂度是O(n^2)。由于dp[i][j]和区间内的所有子状态都有关,所以空间复杂度无法再次进行优化。
2、分治:我觉得虽然叫做分治,其实也算是一道DFS+记忆。也就是我们在递归求解的过程中,如果发现子问题已经被解决了,就直接利用;否则就先解决子问题。而判断子问题是否被解决的方法就是看dp[low][high]是否大于0,如果大于0,就说明已经被计算出来了,就直接利用。所以分支从时间和空间复杂度上和动态规划都一样,甚至本质都是一样的。但是由于分治涉及到了递归调用,所以在实际运行中会花费更多的时间。
代码:
1、动态规划:
class Solution {
public:
int maxCoins(vector<int>& nums) {
if (nums.size() == 0) {
return 0;
}
int n = nums.size(), temp = 0;
nums.insert(nums.begin(), 1), nums.push_back(1);
vector<vector<int>> dp(nums.size() + 1, vector<int>(nums.size() + 1, 0));
for(int len = 1; len <= n; ++len) {
for(int left = 1; left + len <= n + 1; ++left) {
int right = left + len - 1;
for(int k = left; k <= right; ++k) {
temp = nums[left - 1] * nums[k] * nums[right + 1] + dp[left][k - 1] + dp[k + 1][right];
dp[left][right] = max(temp, dp[left][right]);
}
}
}
return dp[1][n];
}
};
2、分治:
class Solution {
public:
int maxCoins(vector<int>& nums) {
if (nums.size() == 0) {
return 0;
}
nums.insert(nums.begin(), 1), nums.push_back(1);
vector<vector<int>> dp(nums.size() + 1, vector<int>(nums.size() + 1, 0));
return divide(nums, dp, 1, nums.size() - 2);
}
private:
int divide(vector<int>& nums, vector<vector<int>>& dp, int low, int high) {
if(low > high) {
return 0;
}
if(dp[low][high] > 0) { // keypoint: the value has already been calculated
return dp[low][high];
}
int ret = 0;
for(int i = low; i <= high; ++i) {
int temp = nums[low - 1]*nums[i]*nums[high + 1] + divide(nums, dp, low, i - 1) + divide(nums, dp, i + 1, high);
ret = max(ret, temp);
}
dp[low][high] = ret;
return ret;
}
};