问题描述
给定n个气球,每个气球对应一个编号,用数组nums[0…n-1]存放,打爆一个气球能够获得金币数:nums[left] * nums[i] * nums[right],然后这个数被移除,left和right变为相邻。求能够获得的最多的金币数量。
注:①假设nums[-1] = nums[n] = 1
② 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
例: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
这也是一道可用DP解答的问题。
这个气球将要获得的金币数跟已经爆掉的那些气球没关(数组中已经把爆掉的气球删除),因此子问题划分时,不是要知道第一个应该爆掉的气球,而是要知道最后一个爆掉的气球。
dp[l][r]:表示爆炸这些气球nums[l…r]的最大金币数。
上述例子中,为方便最末端的两个气球爆炸,我们分别再补两个“1”,nums=[1,3,1,5,8,1]。首先l+3≤r才有意义,所以我们可以设置一个变量len从3开始,循环到数组长度;然后l从0开始移动这个长为len的窗口;最后i指向这个窗口中最后一个爆炸的气球,从l+1开始遍历直到r-1。下图表示最后一个气球爆的8。
初始化:考虑边界情况,dp[0]=0
状态转移方程:注意要时刻更新dp[l][r]的值
dp[l][r] = max( dp[l][r] , nums[l] * nums[i] * nums[r] + dp[l][i] + dp[i][r] )返回值:dp[0][n-1]
class Solution {
public:
int maxCoins(vector<int>& nums) {
nums.insert(nums.begin(), 1);
nums.push_back(1);
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n));
for(int len=3; len <= n; len++){
for(int l=0; l <= n-len; l++){
int r = l+len-1;
for(int i=l+1; i<r; i++){
dp[l][r] = max(dp[l][r],(dp[l][i]+dp[i][r]+nums[l]*nums[i]*nums[r]));
}
}
}
return dp[0][n-1];
}
};