当前位置:   article > 正文

力扣312. 戳气球_力扣 戳气球

力扣 戳气球

题目

有 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

示例 2:

输入:nums = [1,5]

输出:10

思考过程

这道题如果思考整个过程会很麻烦,因为我们不知道戳破某个气球后,谁跟谁挨着,也就没办法求这么简单的公式:a*b*c.

不妨反过来想,如果有两个气球没别戳破,那么这两个气球中间的所有气球多有可能跟他们相连,形成a,b,c这种结构,所以这道题就变成了两个气球固定的情况下,选他俩之间哪个气球作为新插入的气球,让这两个气球求得最终和最大。

加入当前选择的是k位置气球加入

memo[left][right]=Math.max(memo[left][rigth],data[left]*data[right]*data[k]+memo[left][k]+memo[k][right]);

memo[left][k] 这个的含义是在pos>=left+1& pos <k-1这个位置中,能够获得的最大和。

memo[k][right] 这个的含义是在>=left+1 &pos<right这个位置中,能够获得公式计算之后的最大和。

所以这道题有两种解题思路

1、从上到下的记忆化搜索算法。

2、从下到上的dp算法

一、记忆化搜索算法

这所以要用记忆化搜索算法就是为了避免重复计算超时,在我们计算过得位置用记忆数据计算该区域的最大值,如果该值已经计算过就直接返回。

记忆化搜索算法是一个从上到下的算法,我们第一步要确定总的计算范围,

也就是(0,n-1)

然后按照从上下的原则拆分到最小步骤,

1、如果开始,结束位置之间没有气球,则直接返回0

2、如果这中间夹杂着一个气球,则求这个气球的值(a[i-1]*a[i]*a[i+1]),返回

3、如果其中夹杂着多个气球,那么就求每一个气球的公式值.自求(left,k),(k,right)这两部分的和相加,就是最终添加第k个气球后最大值,而memo[left][right]就是再求调整该添加的球位置调整的情况下,最终获得的最大结果。

  1. int[] data;
  2. int[][] memo;
  3. public int maxCoins(int[] nums) {
  4. //方面计算扩展两个位置,开头和结尾都加一个1
  5. int n = nums.length + 2;
  6. data = new int[n];
  7. data[0] = data[n - 1] = 1;
  8. for (int i = 0; i < nums.length; i++) {
  9. data[i + 1] = nums[i];
  10. }
  11. //记忆化搜索方程,用于存放已经计算好的最大和。
  12. memo = new int[nums.length + 2][nums.length + 2];
  13. for (int i = 0; i < nums.length; i++) {
  14. Arrays.fill(memo[i], -1);
  15. }
  16. return solve(0, n - 1);
  17. }
  18. private int solve(int begin, int end) {
  19. // 中间不夹球
  20. if (begin + 1 >= end) {
  21. return 0;
  22. }
  23. //已经计算过这个范围的最大和,直接返回。
  24. if (memo[begin][end] != -1) {
  25. return memo[begin][end];
  26. }
  27. //中间夹了很多球
  28. for (int i = begin + 1; i < end; i++) {
  29. //首先计算夹了这个求之后和的变化。
  30. int sum = data[begin] * data[i] * data[end];
  31. //计算不夹这个求的时候这个范围最大和。
  32. sum += solve(begin, i) + solve(i, end);
  33. //每次求得的值,获取最大的给该区域的记忆化数组
  34. memo[begin][end] = Math.max(memo[begin][end], sum);
  35. }
  36. //到这里,所有的夹球位置都计算过了,也就是知道了begin,end范围的最大值了。
  37. return memo[begin][end];
  38. }

二、dp算法

看记忆化搜索之后,我们发现如果从底向上,也是能够获得了,其中记忆化搜索方法的memo就是dp的方程式,其表示某个范围最大的方程式和。

dp的思想就是从小范围向大范围扩展,一开始这个范围没有求,值给sum贡献0,然后

求夹一个球的总和,

求夹两个球的总和。

.............

dp[i][j]=Math.max(dp[i][j],dp[i+1][k]+dp[k][j]+data[k-1]*data[k]*data[k+1]);

  1. public int maxCoins1(int[] nums) {
  2. int n = nums.length + 2;
  3. data = new int[n];
  4. data[0] = data[n - 1] = 1;
  5. for (int i = 0; i < nums.length; i++) {
  6. data[i + 1] = nums[i];
  7. }
  8. //dp方程,初始化每一个位置为0
  9. memo = new int[nums.length + 2][nums.length + 2];
  10. for (int i = 0; i < nums.length; i++) {
  11. Arrays.fill(memo[i], 0);
  12. }
  13. //从夹球数最少,到夹球数最多这个方向遍历
  14. for(int left=nums.length-1;left>=0;left--){
  15. for(int right=left+2;right<=nums.length+1;right++){
  16. //遍历每一个新加入的求,为区间贡献的sum值,sum要再加上其之外的区间贡献的值
  17. //才是完整的该区域的和是什么。不要漏掉其余区间的和。
  18. //可以理解为新加入的求的贡献+没有它的时候该区间被分成左右两部分的时候和
  19. //这两个值才是区间的完整和。
  20. for(int k=left+1;k<right;k++){
  21. int sum =data[left]*data[right]*data[k] ;
  22. sum+=memo[left][k]+memo[k][right];
  23. memo[left][right]=Math.max(memo[left][right],sum);
  24. }
  25. }
  26. }
  27. return memo[0][n-1];
  28. }

复杂度分析

设小球的个数为n,则时间复杂度为O(n*n*n)

空间复杂度为O(n*n)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/717688
推荐阅读
相关标签
  

闽ICP备14008679号