Burst Balloons
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.
Example:
Input: [3,1,5,8]
Output: 167
Explanation: 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
Note:
- You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
- 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
如题意,戳破一个气球,就会得到该气球中的数值与其相邻气球中数值的乘积数量的硬币,然后气球会被破坏。本题要求按怎样的顺序戳气球,会使得得到的硬币总数最多?
显然,我们可以采取DP的思路,假设某个气球最后被戳破,,然后假设在它之上是哪个气球被戳破。。。列举出所有的可能,去其中的最大值,自然可以得解。然而这种直接DP的方式的时间复杂度在O(n!)级别,非常的高。有没有更好的办法呢?
我们可以发现,其实每次求解的过程是类似的,我们可以尝试使用“分而治之”的思想。如何划分子过程呢?如果按照当前戳破的气球划分的话,戳破后产生的相邻气球会被分割到左右两个子过程中,而这显然是不行的。
我们应该以最后被戳破的气球来划分。为什么呢?因为以最后被戳破的气球来划分,不会将相邻的气球分割到左右两个子过程中(因为它已经是最后一个剩下的气球了)。
按照题目要求,序列的左右应该还有两个值为1 的“虚拟气球”, 便于我么进行乘法运算,则需要修改一下原数组:
nums.insert(nums.begin(),1);
nums.insert(nums.end(),1);
新建一个二维数组dp,其中的值dp[i][j]代表了从数组中的i到j的子数组中可以得到的最大硬币数:
vector<vector<int>> dp(nums.size(),vector<int>(nums.size(),0));
附上完整完整代码:
class Solution {
public:
int maxCoins(vector<int>& nums) {
int N = nums.size();
if(N == 0) return 0;
nums.insert(nums.begin(),1);
nums.insert(nums.end(),1);
vector<vector<int>> dp(nums.size(),vector<int>(nums.size(),0));
for(int len = 1; len <= N; len ++) {
for(int left = 1; left <= N - len + 1; left ++) {
int right = left + len - 1;
for(int i = left;i <= right; i ++) {
dp[left][right] = max(dp[left][right],nums[left-1]*nums[i]*nums[right+1] + dp[left][i-1]+dp[i+1][right]);
}
}
}
return dp[1][N];
}
};
附上LeetCode链接:https://leetcode.com/problems/burst-balloons/