这也能运行?!响应号召做掘金每日好题3.3.3|刷题打卡

163 阅读2分钟

一、题目描述:

二、思路分析:

这题最开始我以为就是区间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 春招闯关活动」, 点击查看 活动详情