Difficulty:Hard
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
解题思路:
解法一(递归 超时):
拿到题目首先想到的是利用循环,对每一位进行循环,在尝试每一位之后再选出最大的一组burst的方式。但是利用循环在这题中存在一定的难度,因为每去掉一位数字就要进行下一次循环,这样最终完成需要多重循环,这样的代码编写过程过于复杂。
所以首先考虑使用递归的方法实现。从第一层来看,循环尝试burst每一位所得到结果,
int t=getarr(arr,left,right-1);
arr.insert(arr.begin()+i,number);
t为burst该位置的数字之后产生的子向量返回的子问题下层递归得到的结果,但是采用递归很麻烦的地方在于去掉一个位置的数字如果采用将该位置置为0,这样在后续的问题计算中将会导致时间额外的浪费,而如果采用在向量中删除这个元素,之后再次进行恢复就可以使时间复杂度从O(n^n)缩减到N!
int number=arr[i];
vector<int>::iterator it=arr.begin()+i;
arr.erase(it);
int t=getarr(arr,left,right-1);
arr.insert(arr.begin()+i,number);
但是采用递归得到的O(n!)时间复杂度还是很慢,达不到题解要求的O(n^2)的时间代价
代码如下:
int getarr(vector<int>& arr,int left,int right);
class Solution {
public:
int maxCoins(vector<int>& nums) {
vector<int> sarr;
sarr.push_back(1);
int size=nums.size();
for(int i=0;i<size;i++)
{
sarr.push_back(nums[i]);
}
sarr.push_back(1);
int left=0;int right=size+1;
int ans=getarr(sarr, left, right);
return ans;
}
};
int getarr(vector<int>& arr,int left,int right)
{
if(left+1>=right) return 0;
else if(left+1<right)
{
int max=0;
for(int i=left+1;i<right;i++)
{
int number=arr[i];
vector<int>::iterator it=arr.begin()+i;
arr.erase(it);
int t=getarr(arr,left,right-1);
arr.insert(arr.begin()+i,number);
int temp=arr[i-1]*arr[i]*arr[i+1]+t;
if(temp>max) max=temp;
}
return max;
}
return 0;
}
解法二(分治动态规划):
如果采用递归方法就会产生很多的重复计算,采用分治动态规划可以省去很多的重复计算,并且在时间复杂度上可以达到O(n^2),从底层开始,传递方程为
dparr[left][right]=(arr[t]*arr[left]*arr[right]+dparr[left][t]+dparr[t][right]);
其中dparr[i][j]表示从i到j之间的最大的数值,当j++时就能使用上一层计算出的结果,所以省去了很多在2^n层面上的计算时间。
动态规划因为是采用从下向上的方法,底层的很多计算可以省略,避免了重复。
代码如下:
class Solution {
public:
int maxCoins(vector<int>& nums) {
int size=nums.size();
int n=size+2;
int *sarr=new int[n+2];
for(int i=0;i<size;i++)
{
sarr[i+1]=nums[i];
}
sarr[0]=1;sarr[size+1]=1;
int dparr[n][n]={};
for(int i=2;i<n;i++)
{
for(int left=0;left<n-i;left++)
{
int right=left+i;
for(int t=left+1;t<right;t++)
{
if(dparr[left][right]<(sarr[t]*sarr[left]*sarr[right]+dparr[left][t]+dparr[t][right]))
{
dparr[left][right]=(sarr[t]*sarr[left]*sarr[right]+dparr[left][t]+dparr[t][right]);
}
}
}
}
int ans=dparr[0][n-1];
return ans;
}
};