【LeetCode】312. 戳气球

article/2025/9/10 16:04:20

312. 戳气球(困难)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解法一:动态规划

首先看一个区间:

在这里插入图片描述

区间(i,j) 是一个开区间,因为我们只能戳爆 i 和 j 之间的气球,不能戳爆索引为 i 和 j 的气球。

我们不妨考虑该区间内被戳爆的最后一个气球,索引记为 k。因为粉色气球是该区间内最后被戳爆的气球,所以在开区间 (i, j) 内除了它没有别的气球了,所以 DP 的状态转移方程只和 i 和 j 位置的数字有关。

在这里插入图片描述

状态定义

定义 dp[i][j] 表示 开区间 (i, j) 内能够得到的最多金币。

状态转移方程

开区间 (i, j) 内得到的最多金币分为三部分:

  • dp[i][k]:开区间 (i, k) 内得到的最多金币
  • nums[i] * nums[k] * nums[j] :戳爆气球 k 可以得到的金币数量
  • dp[k][j] :开区间 (k, j) 内得到的最多金币;

因此最终的状态转移方程为:dp[i][j] = dp[i][k] + nums[i] * nums[k] * nums[j] + dp[k][j];

其中,k 的值可以从 (i, j) 之间任选,所以应该枚举所有的 k 值,选择最大值来更新 dp[i][j]

之后,就从 (i,j) 开区间只有三个数字的时候开始计算,存储每个小区间可以得到的最多金币,然后慢慢扩展到更大的区间,利用小区间里已经算好的数字来算更大的区间。

因此,这道题的第一层遍历是按照区间长度(从3开始,因为我们设置了一个辅助数组,后面会详细解释),第二层遍历是按照左端点的索引,第三层索引是 k 的索引。

初始化

这里设置了一个辅助数字 temp ,用于处理边界问题,当只剩下一个气球的时候,此时的开区间左边界 i = 0, 右边界 j = n+1 ,这两个位置是没有气球的,根据题意,当它们是数字为 1 的气球,所以将它们的值设置为 1 。

因此,我们的区间长度就扩大了 2

所以,当 n = 1 时,区间长度从 3 开始考虑。

最终的返回结果

最后返回开区间(0, n+1) 内的最多金币。

代码

class Solution {
public:int maxCoins(vector<int>& nums) {int n = nums.size();vector<vector<int>> dp(n+2, vector<int>(n+2, 0));vector<int> temp(n+2);// 辅助数组temp[0] = temp[n+1] = 1;for(int i=0; i<n; ++i){temp[i+1] = nums[i];}// len表示开区间长度for(int len = 3; len <=n+2; len++){// i表示开区间的左端点(取不到 所以从0开始)for(int l=0; l<=n-len+2; l++){// 右端点为:r = l+len-1int r = l + len - 1;for(int k=l+1; k<r; ++k){dp[l][r] = max(dp[l][r], dp[l][k] + temp[l]*temp[k]*temp[r] + dp[k][r]);}}}return dp[0][n+1];}
};

思考

阅读大佬解法的时候,发现了他使用了动态规划对例子进行分析,一步步得到答案,这个思路蛮值得我学习的:

在这里插入图片描述

解法二:分治法+记忆化搜索

其实理解完解法一之后,分治法的思想很好理解。

使用分治法时,我们应该考虑的核心问题是「如何用子问题的解来表示原问题的解」,我们把描述子问题的解与原问题的解之间的关系的表达式称为状态转移方程,这里和动态规划是一致的。毕竟,分治法就是分而治之,最后自底向上的合并过程就是动态规划的解决过程。

子问题的思考

按照一般的思路,首先思考能不能直接将[3, 1, 5, 8]直接分为类似于[3]、[1, 5, 8]这样两个子问题。这意味着,我们强行认为必须戳完 [3] 才能戳 [1, 5, 8],或者戳完[1, 5, 8]才能戳[3],显然这不科学。即这两个子问题之间产生了依赖

如何消除依赖呢?我们一开始的想法是先假设第一个被戳爆的气球为x,则x两边的气球则产生了依赖;那我们假设不戳爆x,则x两边的气球就没有了依赖关系!这个气球x,我们可以放在最后戳爆它

这样,对于[3, 1, 5, 8],假设最后戳爆5,则问题就被划分为如下图所示的两个子问题和一个O(1)的问题

在这里插入图片描述

因此,分析到这里,和动态规划就相同了,要想求得大数组的最优解,就必须求得左右两个小数组的最优解。不同的是,在分治法中,我们使用递归来实现。

一样地,也需要遍历开区间(i,j)内的每一个气球,作为最后戳爆的气球。

即:ans = max(ans, maxsubCoins(temp, begin, k, cache) + temp[begin]*temp[k]*temp[end] + maxsubCoins(temp, k, end, cache));

此外,增加 cache 数组保存已经计算过的区间的最优解,避免重复计算。

问题

由于参考的代码是 java,转变成我自己的代码会超时,一直没办法解决,因此正确代码请见参考资料

代码

// 超时
class Solution {
public:int maxsubCoins(vector<int>& temp, int begin, int end, vector<vector<int>> cache){// 说明剩下的两个气球相邻 无法戳破if(begin + 1 == end){return 0;}// 在缓存中查找,避免重复计算if(cache[begin][end] != 0){return cache[begin][end];}int ans = 0;// 找不到,开始计算for(int k=begin+1; k<end; ++k){ans = max(ans, maxsubCoins(temp, begin, k, cache) + temp[begin]*temp[k]*temp[end] + maxsubCoins(temp, k, end, cache));}cache[begin][end] = ans;return ans;}int maxCoins(vector<int>& nums) {int n = nums.size();vector<vector<int>> cache(n+2, vector<int>(n+2, 0));// 辅助数组的写法2nums.insert(nums.begin(),1);nums.push_back(1);return maxsubCoins(nums, 0, n+1, cache);}
};

参考资料

  1. Burst Balloons(leetcode戳气球,困难)从指数级时间复杂度到多项式级时间复杂度的超详细优化思路(回溯到分治到动态规划)
  2. 解题关键——“虚拟气球”,回溯,分治,动态规划。

http://chatgpt.dhexx.cn/article/FVkdGpTD.shtml

相关文章

LeetCode312:戳气球

要求 有 n 个气球&#xff0c;编号为0 到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。戳破第 i 个气球&#xff0c;你可以获得 nums[i - 1] * nums[i] * nums[i 1] 枚硬币。 这里的 i - 1 和 i 1 代表和…

【动态规划】LeetCode 312. 戳气球 --区间DP问题

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

Golang每日一练(leetDay0105) 最小高度树、戳气球

目录 310. 最小高度树 Minimum Height Trees &#x1f31f;&#x1f31f; 312. 戳气球 Burst Balloons &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一…

Leetcode.312 戳气球

题目链接 Leetcode.312 戳气球 题目描述 有 n个气球&#xff0c;编号为0到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums中。 现在要求你戳破所有的气球。戳破第 i 个气球&#xff0c;你可以获得 n u m s [ i − 1 ] ∗ n u m s [ i ] ∗ …

LeetCode 312. 戳气球(Java)

题目 有 n 个气球&#xff0c;编号为0 到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。戳破第 i 个气球&#xff0c;你可以获得 nums[i - 1] * nums[i] * nums[i 1] 枚硬币。 这里的 i - 1 和 i 1 代表和…

每日一道算法题(3)--戳气球问题

题目 戳气球问题 :输入一个包含非负数整数的数组nums代表一排气球, nums[i]代表第i个气球的分数。现在, 你要戳破所有的气球,请计算出最高的分数。 分数的计算规则比较特别&#xff0c;当戳破第i个气球时, 可以获得nums[left] * nums[right] * nums[i]的分数&#xff0c;其中nu…

【leetcode】312.戳气球 (超详细解析,动态规划)

312. 戳气球 难度困难 有 n 个气球&#xff0c;编号为0 到 n-1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。如果你戳破气球 i &#xff0c;就可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 lef…

戳气球问题

戳气球问题本质上可以认为是一种组合逆推的问题&#xff08;背包问题模型&#xff09; 我们可以有两种方法 1.dfs爆搜法 2.dp法 1.dfs爆搜法 dfs爆搜就是暴力枚举出哪个先破&#xff0c;哪个后破&#xff0c;直到找出最佳的方案 本质上是一个全排列问题&#xff0c;但是全…

Linux修改主机名—主机名和IP映射

一、修改主机名 /etc/sysconfig/network 二、主机名和IP映射 /etc/hosts Windows 里面的路径&#xff1a;C:\Windows\System32\drivers\etc

Linux系统下如何修改主机名

Linux系统安装好后&#xff0c;都会有默认的主机名&#xff0c;这里以CentOS系统为例&#xff0c;默认的主机名为localhost.localdomain&#xff0c;为了便于使用&#xff0c;我们常常需要修改主机名&#xff0c;下面演示的是永久更改主机名的方法。 方法/步骤 以根用户登录&a…

杂记 | Linux修改主机名(不用重启)

新买的云服务器主机名比较奇怪。强迫症难受&#xff0c;于是想通过Linux命令修改主机名&#xff0c;且选择最轻松的方案&#xff08;不想重启系统&#xff09;&#xff0c;使用以下命令即可&#xff1a; hostnamectl set-hostname 主机名 systemctl restart systemd-hostnamed…

修改Linux的主机名

修改主机名 新申请下来的设备&#xff0c;修改root后面的主机名为一连串的字符和数字用于centos7.X&#xff0c;其他版本未验证 修改主机名 hostnamectl set-hostname Test_Server1修改后可查看是否修改成功 cat /etc/hostname也可以通过修改hostname文件修改主机名未生效可…

Linux 永久修改主机名(转载)

Linux修改主机名&#xff0c;永久生效。 linux查看主机名: 查看主机命令&#xff1a; [rootlinux_epm2 ~]# hostname localhost.localdomain localhost.localdomain即为默认的主机名。 修改network文件: 用root用户登录&#xff0c;如果不是root用户&#xff0c;使用su命令切换…

Linux修改主机名(hostname)

查看当前的主机名 #第一种hostname #第二种hostnamectl修改主机名 执行修改命令&#xff1a; hostnamectl set-hostname <new-hostname>编辑hosts文件 vim /etc/hosts

Linux修改主机名(静态主机名、临时主机名)

背景 通常情况下Linux在安装时会设置主机名、root密码等相关参数&#xff0c;但安装后的使用过程中或许你需要修改主机名&#xff0c;本文讲述如何修改主机名&#xff0c;包括临时修改和永久修改。 查看主机名 原生态的Linux一般自带两个命令&#xff1a;hostname、hostname…

linux修改主机名命令

一、 使用hostname命令 比如我现在的主机名是haozhikuan-hbza&#xff0c;如果我想把主机名变成hbza-hbza可以用 hostname hbza-hbza 然后 hostname 或者uname -u都可以查看自己的主机名&#xff0c;如果再新打开一个终端就能看到自己的主机名已经修改过变成hbza-hbza了。 不…

Linux修改主机名永久生效

Linux&#xff08;centos7非此方法&#xff09; 修改主机名&#xff0c;永久生效。 linux查看主机名: 查看主机命令&#xff1a; [rootlinux_epm2 ~]# hostname localhost.localdomain localhost.localdomain即为默认的主机名。 修改network文件: 用root用户登录&#xf…

Linux修改主机名和域名

在/etc里有一个hostname这个是主机名存放文件&#xff0c;修改主机名就在这里面修改&#xff0c;用命令&#xff1a; vim /etc/hostname 进入后直接按下键盘上的D键&#xff0c;一下不行就两下&#xff0c;D键就将原有的主机名给删了&#xff08;D键可以一次性将光标所处的那一…

Linux修改主机名--立即生效的方法

Linux修改主机名–立即生效的方法 在使用Linux操作系统过程中&#xff0c;如果我们想更改主机名时&#xff0c;可以用以下命令进行操作 查看主机名 [rootecs-5332 ~]# hostname ecs-5332修改主机名&#xff0c;【myhost】可以替换为自己想要的主机名 [rootecs-5332 ~]# hos…

Linux永久修改主机名和IP

Linux永久修改主机名和IP 文章目录 Linux永久修改主机名和IP一、修改主机名1.查看当前主机名2.修改主机名的配置文件3.要想通过主机名访问&#xff0c;还需要修改一个配置文件4.然后执行reboot重启电脑5.再次执行hostname是否是你修改后的主机名 二、设置静态ip1.查看本机的配置…