要求

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

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

思路

只要涉及求最值,没有任何奇技淫巧,一定是穷举所有可能的结果,然后对比得出最值。
穷举主要有两种算法,就是回溯算法和动态规划,前者就是暴力穷举,而后者是根据状态转移方程推导「状态」

方法一:回溯算法
我们其实就是想穷举戳气球的顺序,不同的戳气球顺序可能得到不同的分数,我们需要把所有可能的分数中最高的那个找出来

class Solution {  //O(n!) 超时public int maxCoins(int[] nums) {int[] state = new int[nums.length];for(int i = 0;i<nums.length;i++){state[i] = 0;}tryGetCoins(nums,state,0);return max;}int max = 0;void tryGetCoins(int[] nums, int[] state,int current){int[] tempState = state.clone();int i = 0;if(finished(state)){max = Math.max(max,current);return;}for(i=0;i<nums.length;i++){if(state[i] == 1)continue;tempState[i] = 1;tryGetCoins(nums,tempState,current+getCoinsAt(nums,state,i));copyArray(tempState,state);}}int getCoinsAt(int[] nums,int[] state,int i){int j = 0,result = nums[i];for(j = i-1;j>-1;j--){if(state[j] == 0){result *= nums[j];break;}}for(j = i+1;j<state.length;j++){if(state[j] == 0){result *= nums[j];break;}}return result;}void copyArray(int[] dest,int[] origin){for(int i = 0; i<dest.length;i++){dest[i] = origin[i];}}boolean finished(int[] state){boolean finished = true;for(int i = 0; i < state.length;i++){if(state[i] == 0)finished = false;}return  finished;}
}

当我们回溯完所有的戳法之后,就能找出最大得分。但是该回溯法的时间复杂度显然为O(n!),而气球个数的取值为[0,500],必然会有超时的问题。

方法二:动态规划
我们每戳破一个气球 nums[i],得到的分数和该气球相邻的气球 nums[i-1] 和 nums[i+1] 是有相关性的。但运用动态规划算法的一个重要条件:子问题必须独立。所以必须巧妙地定义 dp 数组的含义,避免子问题产生相关性,才能推出合理的状态转移方程。
数组两端加入新的虚拟气球,形成一个新的数组 points,现在气球的索引变成了从 1 到 n,points[0] 和 points[n+1] 可以认为是两个「虚拟气球」

状态定义:dp[i][j] = x 表示,戳破气球 i 和气球 j 之间(不包括 i 和 j)的所有气球,获得的最高分数为 x。 base case 就是 dp[i][j] = 0,其中 0 <= i <= n+1, j <= i+1,开区间 (i, j) 中间没有气球可以戳。

// base case 已经都被初始化为 0
int[][] dp = new int[n + 2][n + 2];


推导状态转移方程:
假设 k 是气球 i 和气球 j 之间的所有气球最后被戳破的那一个,需要先把开区间 (i, k) 的气球都戳破,再把开区间 (k, j) 的气球都戳破;最后剩下的气球 k,相邻的就是气球 i 和气球 j,这时候戳破 k 的话得到的分数就是 points[i]*points[k]*points[j]。
dp[i][j] 的值应该为:dp[i][k] + dp[k][j] + points[i]*points[k]*points[j]

由于是开区间,dp[i][k] 和 dp[k][j] 不会影响气球 k;而戳破气球 k 时,旁边相邻的就是气球 i 和气球 j 了,最后还会剩下气球 i 和气球 j,这也恰好满足了 dp 数组开区间的定义。
对于一组给定的 i 和 j,我们只要穷举 i < k < j 的所有气球 k,选择得分最高的作为 dp[i][j] 的值即可
dp[i][j] 所依赖的状态是 dp[i][k] 和 dp[k][j],那么我们必须保证:在计算 dp[i][j] 时,dp[i][k] 和 dp[k][j] 已经被计算出来了(其中 i < k < j)。


选择从下往上遍历

public class LeetCode312 {public int maxCoins(int[] nums) {int n = nums.length;//添加两侧的虚拟气球int[] points = new int[n + 2];points[0] = points[n+1] = 1;//遍历数组赋值for (int i =1; i <= n ; i++) {points[i] = nums[i-1];}//base case初始化为0int[][] dp = new int[n+2][n+2];//i从下往上升for (int i = n; i >= 0; i--) {//j从从左往右for (int j = i + 1; j < n + 2; j++) {//戳破哪个气球for (int k = i + 1; k < j; k++) {//则优做选择dp[i][j] = Math.max(dp[i][j],dp[i][k] + dp[k][j] + points[i]*points[j]*points[k]);}}}return dp[0][n+1];}
}

LeetCode312:戳气球相关推荐

  1. LeetCode312 戳气球

    回溯思想 很显然涉及求最值,没有任何奇技淫巧,一定是穷举所有可能的结果,然后对比得出最值 所以说,只要遇到求最值的问题,首先要思考的就是:如何穷举所有可能的结果 穷举主要有两种算法:就是回溯算法和动态 ...

  2. leetcode312. 戳气球(动态规划)

    有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中. 现在要求你戳破所有的气球.如果你戳破气球 i ,就可以获得 nums[left] * nums[i] ...

  3. 戳气球--LeetCode312

    戳气球–LeetCode312 题目 有 n个气球,编号为0到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums中. 现在要求你戳破所有的气球.每当你戳破一个气球 i时,你可以获得 num ...

  4. 312. Burst Balloons 戳气球

    Title 有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中. 现在要求你戳破所有的气球.如果你戳破气球 i ,就可以获得 nums[left] * nu ...

  5. gdut-与蓝神一起戳气球 hnust-硬币翻转 - 博弈论

    与蓝神一起戳气球 题目描述 As you know,13-14!=-1.(^@^) 但是蓝神偏不信.所以,现在问题来了.你要与蓝神通过我们最喜欢的游戏--戳气球一决胜负. 现有n个气球,你和蓝神轮流戳 ...

  6. Leetcode.312 戳气球

    题目链接 Leetcode.312 戳气球 题目描述 有 n个气球,编号为0到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums中. 现在要求你戳破所有的气球.戳破第 i 个气球,你可 ...

  7. 【LeetCode】312. 戳气球

    312. 戳气球(困难) 解法一:动态规划 首先看一个区间: 区间(i,j) 是一个开区间,因为我们只能戳爆 i 和 j 之间的气球,不能戳爆索引为 i 和 j 的气球. 我们不妨考虑该区间内被戳爆的 ...

  8. leetcode: 312. 戳气球

    312. 戳气球 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/burst-balloons/ 有 n 个气球,编号为0 到 n - 1,每个气球上 ...

  9. leetcode:戳气球 leetcode:零钱兑换

    戳气球题目描述 有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中. 现在要求你戳破所有的气球.每当你戳破一个气球 i 时,你可以获得 nums[left] ...

最新文章

  1. oracle执行先决条件检查失败的解决方法
  2. 四川网络推广介绍搜索引擎从哪几个方面判断网站质量好坏?
  3. java super extends_Java继承extends与super关键字
  4. @Autowired静态变量
  5. 【C/C++和指针】深度解析---指针与数组 【精华】
  6. asp单元格合并后宽度没有合并_宅在家里跟着大牛从零开始学excel第五课-合并,边框,列宽行高...
  7. sysbench的框架实现介绍
  8. itextpdf添加表格元素_itext生成pdf文件-表格
  9. Python稳基修炼的经典案例3(计算机二级、初学者必须掌握的例题)
  10. 中原工学院计算机组成原理试卷,中原工学院计算机组成原理试卷.doc
  11. 《细说PHP》第四版 样章 第二章 PHP的应用与发展 1
  12. 南京工程学院计算机博士,南京工程学院高层次人才引进工作实施办法(修订)...
  13. 《天道》中最智慧的4句话,看懂改变一生
  14. matlab 数值积分求面积,用几种数值积分的方法计算地图面积
  15. html页面清空标签里的内容
  16. 以太坊_Mist客户端下载及安装
  17. html5 3d引擎 星空,使用3D引擎threeJS实现星空粒子移动效果
  18. 安装双系统时进行多重引导,最好先安装Windows再装Linux
  19. 就地初始化与列表初始化
  20. 基于UnifierP6的4D,5D,nD规划

热门文章

  1. html设置图片与边框的距离,css图片如何设置上边框距离
  2. 无感FOC滑膜观测器学习
  3. 微信支付(扫码支付)微信公众平台开发教程(6)
  4. IOS技术分享| 互动连麦场景实现
  5. 机器学习 day15异常检测
  6. 刮刮奖效果的简单实现
  7. 模式识别第二课 建立MFC窗口+插入图片+处理+显示图片
  8. snipaste滚动截图方法_如何在电脑上截图?教你3种常用的截图方法,利用快捷键效果最快...
  9. Android String字符串截取方法总结
  10. PHP isset()与empty()的区别