一、题目描述:
二、思路分析:
这题最开始我以为就是区间DP,看起来和之前讲的石子合并没区别,就是在计算状态转移方程时因地制宜下就好,怎么也没做出来,有种“这为啥会做错的感觉”,后来慢慢发现这题坑挺多的。
第一个小坑是这个
现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。
对于超出边界,要XXX处理,这类要求就会加大代码的复杂性,过多的ifelse会让你十分烦恼,就会出现这种情况
这里官方题解写的十分优雅,让我们认识到了自己代码如何不体面
vector<int> val(n + 2);
val[0] = val[n + 1] = 1;
第二个坑隐藏在示例中,我们发现气球被戳爆后就不存在了,这和我们的石子合并是完全不一样的,而这种方式就让我们很难从坐标上找到正确的点来计算增长的硬币,使用正向思维是绝无可能解决这个困惑的。
不妨逆向思考问题,从戳气球变成反向制造气球,把l,r当作制造气球的左右边界,而k是第一个制造出来的气球,此时只有l,k,r三个气球,那硬币增长量显然是:sum = nums[l] * nums[k] * nums[r]
三、AC 代码:
class Solution {
public:
int maxCoins(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> rec(n + 2, vector<int>(n + 2));
vector<int> val(n + 2);
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
for(int len = 2; len <= n + 1; len ++){
for(int i = 0; i + len <= n + 1; i++){
int l = i, r = i + len;
for(int k = i + 1; k < r; k++){
int sum = val[l] * val[k] * val[r];
sum += rec[l][k] + rec[k][r];
rec[l][r] = max(rec[l][r], sum);
}
}
}
return rec[0][n + 1];
}
};
四、总结:
有趣的是,当你没想通逆向那步而看题解时,你会发现题解和你的差异只是
sum = nums[k - 1] * nums[k] * nums[k + 1];
sum = nums[l] * nums[k] * nums[r];
的一行代码差别,直接修改就会产生一种**这也能运行?!**的感觉
少见自然会多怪,妄图背个模板就能解决算法题无异于异想天开,希望各位多做题多思考,逢山开路,遇河架桥,啃下动态规划这块硬骨头
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情