力扣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]就是再求调整该添加的球位置调整的情况下,最终获得的最大结果。

int[] data;
    int[][] memo;

    public int maxCoins(int[] nums) {
    	//方面计算扩展两个位置,开头和结尾都加一个1
        int n = nums.length + 2;
        data = new int[n];
        data[0] = data[n - 1] = 1;
        for (int i = 0; i < nums.length; i++) {
            data[i + 1] = nums[i];
        }
    	//记忆化搜索方程,用于存放已经计算好的最大和。
        memo = new int[nums.length + 2][nums.length + 2];
        for (int i = 0; i < nums.length; i++) {
            Arrays.fill(memo[i], -1);
        }
        return solve(0, n - 1);
    }

    private int solve(int begin, int end) {
    	//	中间不夹球
        if (begin + 1 >= end) {
            return 0;
        }
    	//已经计算过这个范围的最大和,直接返回。
        if (memo[begin][end] != -1) {
            return memo[begin][end];
        }
				//中间夹了很多球
        for (int i = begin + 1; i < end; i++) {
      	//首先计算夹了这个求之后和的变化。
            int sum = data[begin] * data[i] * data[end];
      	//计算不夹这个求的时候这个范围最大和。
            sum += solve(begin, i) + solve(i, end);
      	//每次求得的值,获取最大的给该区域的记忆化数组
            memo[begin][end] = Math.max(memo[begin][end], sum);
        }
      	//到这里,所有的夹球位置都计算过了,也就是知道了begin,end范围的最大值了。
        return memo[begin][end];
    }

二、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]);

public int maxCoins1(int[] nums) {

        int n = nums.length + 2;
        data = new int[n];
        data[0] = data[n - 1] = 1;
        for (int i = 0; i < nums.length; i++) {
            data[i + 1] = nums[i];
        }
    	//dp方程,初始化每一个位置为0
        memo = new int[nums.length + 2][nums.length + 2];
        for (int i = 0; i < nums.length; i++) {
            Arrays.fill(memo[i], 0);
        }
      	//从夹球数最少,到夹球数最多这个方向遍历
        for(int left=nums.length-1;left>=0;left--){
            for(int right=left+2;right<=nums.length+1;right++){
                  //遍历每一个新加入的求,为区间贡献的sum值,sum要再加上其之外的区间贡献的值
                  //才是完整的该区域的和是什么。不要漏掉其余区间的和。
                  //可以理解为新加入的求的贡献+没有它的时候该区间被分成左右两部分的时候和
                  //这两个值才是区间的完整和。
                for(int k=left+1;k<right;k++){
                    int sum =data[left]*data[right]*data[k] ;
                    sum+=memo[left][k]+memo[k][right];
                    memo[left][right]=Math.max(memo[left][right],sum);

                }
            }
        }
        return memo[0][n-1];

    }

复杂度分析

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值