戳气球问题

有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。

求所能获得硬币的最大数量。

说明:

你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。
0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
示例:

输入: [3,1,5,8]
输出: 167
解释: nums = [3,1,5,8] –> [3,5,8] –> [3,8] –> [8] –> []
coins = 315 + 358 + 138 + 181 = 167

思路:

可以利用区间动态规划,dp[i][j]表示i到j之间的最优解(不包括i,j),那么dp[i][j]就等于max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[j] * nums[k])。

即我们假设求i到j之间的最优解,k为i和j之间的数,那么当前i到j之间以k为基准将要戳k(也就是k是i到j中最后一个戳的)的最优解就等于k左半部分最优解加k右半部分最优解加k、j、i的乘积,遍历k求出最大的一个就好了。当然dp初始值要为0,这样第一次戳i时dp[i-1][i+1]就理所当然等于0 + nums[i+1] nums[i] nums[i - 1] + 0。

nums首部先插入1,再在末尾补1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
int maxCoins(vector<int>& nums) {
if(nums.empty()){
return 0;
}
nums.insert(nums.begin(), 1);
nums.push_back(1);
dp.resize(nums.size(), vector<int>(nums.size(), 0));
for(int i = 2; i < nums.size(); ++i){
for(int j = 0; j + i < nums.size(); ++j){
for(int k = j + 1; k < j + i; ++k){
dp[j][j + i] = max(dp[j][j + i], dp[j][k] + dp[k][j + i] + nums[k] * nums[j] * nums[j + i]);
}
}
}
return dp[0][nums.size() - 1];
}
private:
vector<vector<int>>dp;
};

纸短情长啊文章结束了但我们的故事还在继续
坚持原创技术分享,您的支持将鼓励我继续创作!