312. 戳气球
Q:
有 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
A:
先上代码,下面解释
class Solution {
public:
int maxCoins(vector<int>& nums)
{
int res = 0;
int n = nums.size();
nums.insert(nums.begin(), 1);
nums.push_back(1); //气球编号1-n,存储在索引1-n
vector<vector<int>> dp(n+2, vector<int>(n+2, 0));
//dp[i][j]表示i到j所有气球爆掉得到的最多金币数
for (int len = 1; len <= n; ++len)
{
for (int start = 1; start + len - 1 <=n; ++start)
{
int cur_max = 0, end = start + len - 1; //[start,end],长度len
for (int boom = start; boom <= end; ++boom)
{
cur_max = max(cur_max, dp[start][boom - 1] + dp[boom + 1][end] + nums[start - 1] * nums[boom] * nums[end + 1]);
}
dp[start][end] = cur_max;
}
}
// for (auto row : dp)
// {
// for (auto col : row)
// {
// printf("%3d", col);
// }
// cout << endl;
// }
return dp[1][n];
}
};
我觉得关键就是dp方程的理解。求i到j父结构的最优解时,我们在中间任意位置k开刀,将k爆掉再求左右子结构的最优解。但这里有个问题,爆了k之后,左边的子结构为i到k-1,右边子结构为k+1到j。拿左边举例,这个子结构的解我们已经求好了(因为我们用的是自底向上的dp),**而这个求好的解是建立在没有爆k的情况下的!**所以我们如果要利用子结构解的话,前提就变成了左右子结构i到k-1和k+1到j都已经爆掉,**即此时k的左右气球应该分别是i-1和j+1。**这就是我对dp方程中加的为什么不是相邻三个气球的值相乘值的解释。
进击的小🐴农

浙公网安备 33010602011771号