力扣:动态规划 有意思的 戳气球

动态规划

世界还是这么小啊,又遇见了我们最亲爱的朋友 ———— 动态规划,怎么说也见过两三次了,回忆起来了我们的这位老朋友了吗?

我来带你回忆回忆我们的动态四部曲!

动态四部曲

1. 确定dp的状态 : 定义为一维二维还是三维?表示什么含义?

2. 确定状态转移方程

3. 初始化,也就是dp数组可以取得的元素的值

4. 自底向上的方式计算dp,并得出最优值(遍历的顺序)

戳气球

题目

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

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。

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

下面是一个示例

示例 1:

输入:nums = [3,1,5,8]
输出: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数组的状态

这里我们所要求的问题的解是戳破气球所能获得的硬币的最大数量,那么这个问题的子问题也就是戳破了nums数组中从ij的气球获得的硬币最大数量,所以我们定义dp数组为二维数组,而dp[i][j]的含义就为ij这个区间所获得的最大硬币数量。

确定状态转移方程

那么这个最大硬币数量又是怎么来的呢?

我们看到示例当中的计算方式,为被戳破的那个气球所表示的数字与其左右两边数字的乘积,我们假定nums数组的三个数字下标i``k``j,被戳爆的气球下标索引为k,那么戳爆k所获得的硬币的这个计算表达式就可以表示为 nums[i] * nums[k] * nums[j]。这样子仅仅就是三个气球的情况,要是左右两边气球更多呢?

那么同样的,假设在 [i, j]区间内戳破一个索引为k的气球,我们就可以推导出 dp[i][j] 的值就是戳破k左边的所有气球得到的dp[i][k] 加上戳破 k 右边边的所有气球得到的dp[k][j],再加上 k 对应的气球得到的 nums[i] * nums[k ] * nums[j],那么相应的状态转移方程就为dp[i][j] = max( dp[i][j],(dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]));

初始化,也就是dp数组可以取得的元素的值

这里dp数组初始化,能取得值只有0了,取不取其实也无关紧要。

4. 自底向上的方式计算dp,并得出最优值(遍历的顺序)

这里很显然是倒序递推了,假设这里是先求解 dp[2][6],那么就需要先求解对应的“dp[4][6]”,如果采用顺序数组循环,那么求解 dp[4][6] 会在求解 dp[2][6] 之前进行,显然就不符合逻辑了。所以这里排成倒序递推更好求解

代码

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 2; j <= n + 1; j++) {
                for (int k = i + 1; k < j; k++) {
                    dp[i][j] = max(
                            dp[i][j],
                            (dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]));
                }
            }
        }
        return dp[0][n + 1];
    }
};
复制代码

代码解析

我们首先需要扩大数组,两边都加上1,因为题目中说明若是右边或者左边没有气球就用1替代,所以这里我们从下标为n - 1处开始倒序递推,而我们在右边元素j从至少间隔i一个位置的下标开始遍历,中间元素k则是要被戳破的气球,也就是i``j之间的元素,并利用状态转移方程,求解,最后求得的dp[0][n + 1]就是我们最终的结果

总结

动态规划题目种类还是很多的,题型也是防不胜防,还是需要多学习。

我是小白,我们一起在学习代码的路上不折不扣,屡战屡败,屡败屡战!

おすすめ

転載: juejin.im/post/7040749005004865544